条件变量(Condition Variable)详解
角色与功能
条件变量在多线程编程中扮演着非常重要的角色,它用于阻塞一个或多个线程,直到某个特定条件为真。这种机制允许线程在等待某个条件成立时释放其占用的资源(如互斥锁),从而避免了忙等待(busy-waiting)造成的资源浪费。当条件满足时,其他线程或同一线程可以被唤醒并继续执行。
条件变量通常与互斥锁(mutex)一起使用,因为多个线程可能会同时等待同一个条件,且需要确保在检查条件(predicate)和修改条件变量状态时互斥访问共享资源。
工作原理
-
等待(Wait):线程在调用条件变量的
wait
函数前,必须先锁定一个互斥锁。wait
函数会原子地释放互斥锁并使当前线程进入睡眠状态,直到另一个线程调用条件变量的notify_one
或notify_all
函数来唤醒它。被唤醒后,线程会重新尝试获取之前释放的互斥锁,然后继续执行。 -
通知(Notify):当条件变量的条件变为真时,另一个线程(或可能是同一个线程在条件被改变后)可以调用
notify_one
来唤醒等待该条件变量的一个线程,或者调用notify_all
来唤醒所有等待的线程。注意,被唤醒的线程不会立即继续执行,它们必须重新获取互斥锁。
使用条件变量进行线程同步的示例
以下是一个使用C++标准库中的std::condition_variable
和std::mutex
进行线程同步的示例。在这个例子中,我们有一个生产者线程和多个消费者线程,它们通过条件变量来同步对共享资源的访问。
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
std::mutex mtx;
std::condition_variable cv;
std::queue<int> q;
bool ready = false; // 用来控制是否开始消费
void producer(int id) {
for (int i = 0; i < 10; ++i) {
std::unique_lock<std::mutex> lck(mtx);
q.push(i);
std::cout << "Producer " << id << " produced " << i << std::endl;
ready = true; // 通知可能有消费者可以开始消费
cv.notify_one(); // 唤醒一个等待的消费者
lck.unlock();
// 模拟生产耗时
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
void consumer(int id) {
std::unique_lock<std::mutex> lck(mtx);
while (!ready) cv.wait(lck); // 等待直到有产品可以消费
while (true) {
while (q.empty()) {
ready = false; // 如果没有产品,重置ready标志
cv.wait(lck); // 等待产品到来
}
int value = q.front();
q.pop();
std::cout << "Consumer " << id << " consumed " << value << std::endl;
// 模拟消费耗时
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
}
int main() {
std::thread producers[10];
std::thread consumers[5];
// 创建生产者线程
for (int i = 0; i < 10; ++i) {
producers[i] = std::thread(producer, i);
}
// 创建消费者线程
for (int i = 0; i < 5; ++i) {
consumers[i] = std::thread(consumer, i);
}
// 等待所有生产者完成
for (auto& t : producers) {
t.join();
}
// 注意:在实际应用中,可能需要一种机制来优雅地停止消费者线程
// 这里仅作为示例,未实现停止消费者的逻辑
return 0;
}
注意:这个示例为了简化而省略了一些重要的细节,比如如何优雅地停止消费者线程。在实际应用中,你可能需要设置一个额外的条件来通知消费者线程停止工作,并且需要确保所有线程都正确处理了共享资源的访问。
此外,由于ready
标志在多个线程之间共享,并且用于控制消费者的启动和停止,因此必须谨慎使用以避免竞