互斥锁
使用条件变量的 wait
函数来等待特定条件
#include <iostream>
#include <mutex>
#include <condition_variable>
#include <thread>
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void worker() {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return ready; }); // 等待 ready 变量为真
std::cout << "Worker thread is running" << std::endl;
}
int main() {
std::thread t(worker);
{
std::lock_guard<std::mutex> lock(mtx);
ready = true; // 设置 ready 变量为真
}
cv.notify_one(); // 唤醒等待的线程
t.join();
return 0;
}
这个例子中定义了一个互斥锁 mtx
,一个条件变量 cv
和一个布尔变量 ready
。worker
函数是一个线程函数,它在一个独立的线程中运行。
在 worker
函数中,首先创建了一个 std::unique_lock
对象 lock
,用来锁定互斥锁。然后调用条件变量的 wait
函数等待特定条件。wait
函数接受两个参数:一个互斥锁和一个谓词函数。谓词函数用来检查特定条件是否满足。在这个例子中,谓词函数是一个 lambda 表达式,它返回 ready
变量的值。
当调用 wait
函数时,它会自动解锁互斥锁并阻塞当前线程,直到条件变量被唤醒。当条件变量被唤醒时,wait
函数会自动锁定互斥锁,并调用谓词函数检查特定条件是否满足。如果谓词函数返回 true
,则表示特定条件已经满足,此时 wait
函数会返回。否则,wait
函数会继续阻塞当前线程,直到条件变量再次被唤醒。
在主函数中,首先创建了一个线程 t
,并将其与 worker
函数关联。然后使用花括号创建了一个临时作用域,在该作用域中创建了一个 std::lock_guard
对象 lock
,用来锁定互斥锁。接着将 ready
变量设置为 true
,表示特定条件已经满足。最后调用条件变量的 notify_one
函数唤醒等待的线程。
需要注意的是,在访问共享变量(如 ready
变量)时,需要使用互斥锁来保护对它的访问。在这个例子中,使用了 std::lock_guard
类来管理互斥锁。当创建一个 std::lock_guard
对象时,它会自动锁定互斥锁;当 std::lock_guard
对象销毁时,它会自动解锁互斥锁。
条件变量的工作原理
可以将条件变量比作一个闹钟,它可以让线程进入睡眠状态,并在特定条件下唤醒线程。当调用条件变量的 wait
函数时,线程就像是一个人躺在床上,按下了闹钟的按钮,然后进入了睡眠状态。此时,线程不会执行任何操作,直到被唤醒。
当其他线程调用条件变量的 notify_one
或 notify_all
函数时,就相当于闹钟响了。此时,等待的线程会被唤醒,就像人被闹钟吵醒一样。当线程被唤醒后,它会检查特定条件是否满足。如果特定条件已经满足,则线程会继续执行;否则,线程会再次进入睡眠状态,继续等待被唤醒。
例如,在上面的例子中,特定条件是 ready
变量为真。当调用 wait
函数时,线程会进入睡眠状态,并等待 ready
变量变为真。当主函数中将 ready
变量设置为真并调用 notify_one
函数时,等待的线程就会被唤醒。此时,它会检查 ready
变量是否为真。由于 ready
变量已经被设置为真,因此特定条件已经满足,此时 wait
函数会返回。
wait自动解锁互斥锁并阻塞当前线程
可以将互斥锁比作一扇门,它可以防止多个线程同时访问共享资源。当一个线程需要访问共享资源时,它需要先锁定互斥锁,就像用钥匙把门锁上一样。当线程访问完共享资源后,它需要解锁互斥锁,就像用钥匙把门打开一样。
而条件变量可以比作一个闹钟,它可以让线程进入睡眠状态,并在特定条件下唤醒线程。当调用条件变量的 wait
函数时,线程就像是一个人躺在床上,按下了闹钟的按钮,然后进入了睡眠状态。此时,线程不会执行任何操作,直到被唤醒。
当调用 wait
函数时,它会自动解锁互斥锁并阻塞当前线程。这就像一个人躺在床上,按下了闹钟的按钮,然后起身去开门,把门打开,再回到床上继续睡觉。此时,其他线程就可以进入房间(锁定互斥锁)并访问共享资源。
当其他线程调用条件变量的 notify_one
或 notify_all
函数时,就相当于闹钟响了。此时,等待的线程会被唤醒,就像人被闹钟吵醒一样。当线程被唤醒后,它会起身去关门(锁定互斥锁),然后检查特定条件是否满足。如果特定条件已经满足,则线程会继续执行;否则,线程会再次进入睡眠状态,继续等待被唤醒。
共享资源是房间里的一个东西吗
是的,在这个比喻中,共享资源可以比作房间里的一个东西,例如一张桌子或一个柜子。多个线程可以比作多个人,它们都想进入房间并使用这个共享资源。
由于共享资源只能被一个线程(人)同时访问,因此需要使用互斥锁(门)来防止多个线程(人)同时访问共享资源。当一个线程(人)需要访问共享资源时,它需要先锁定互斥锁(关上门),然后才能访问共享资源。当线程(人)访问完共享资源后,它需要解锁互斥锁(打开门),以便其他线程(人)可以进入房间并访问共享资源。