std::condition_variable
std::condition_variable
是 C++11 标准库中提供的一个同步原语,用于线程间的条件等待和通知机制。它允许一个或多个线程等待某个条件变为真(即被满足),并通过另一个线程的通知来唤醒这些等待的线程。
主要特点
- 条件等待:线程可以等待某个条件变量,直到其他线程通知该条件已被满足。
- 通知机制:支持单个通知 (
notify_one
) 和全部通知 (notify_all
)。 - 与互斥锁结合使用:
std::condition_variable
需要与std::mutex
结合使用,以确保线程安全。
成员函数
以下是 std::condition_variable
的主要成员函数:
-
等待函数:
void wait(std::unique_lock<std::mutex>& lock);
:阻塞当前线程,直到条件变量被通知。template<typename Predicate> void wait(std::unique_lock<std::mutex>& lock, Predicate pred);
:阻塞当前线程,直到条件变量被通知且谓词返回true
-
通知函数:
void notify_one();
:唤醒一个等待该条件变量的线程(如果有)。
void notify_all();
:唤醒所有等待该条件变量的线程。
使用步骤
- 创建互斥锁:通常使用
std::mutex
或其派生类。 - 创建条件变量:使用
std::condition_variable
。 - 获取互斥锁:在进入等待状态之前,必须先锁定互斥锁。
- 等待条件变量:调用
wait
方法等待条件变量被通知。 - 修改共享数据:在另一个线程中修改共享数据,并在修改后通知条件变量。
- 通知等待的线程:调用
notify_one
或notify_all
方法通知等待的线程。
使用示例
示例 1:生产者-消费者问题
这是一个经典的生产者-消费者问题,展示了如何使用 std::condition_variable
来同步生产者和消费者线程
#include <iostream> #include <queue> #include <thread> #include <mutex> #include <condition_variable> std::queue<int> queue; std::mutex mutex; std::condition_variable cond_var; const int max_queue_size = 10; void producer(int id) { for (int i = 0; i < 20; ++i) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); // 模拟生产时间 std::unique_lock<std::mutex> lock(mutex); cond_var.wait(lock, [] { return queue.size() < max_queue_size; }); // 等待队列不满 queue.push(i); std::cout << "Producer " << id << " produced: " << i << std::endl; lock.unlock(); cond_var.notify_one(); // 通知消费者 } } void consumer(int id) { while (true) { std::unique_lock<std::mutex> lock(mutex); cond_var.wait(lock, [] { return !queue.empty(); }); // 等待队列不为空 if (!queue.empty()) { int value = queue.front(); queue.pop(); std::cout << "Consumer " << id << " consumed: " << value << std::endl; if (value == 19) { break; // 当最后一个元素被消费时退出循环 } } lock.unlock(); cond_var.notify_one(); // 通知生产者 } } int main() { const int num_producers = 2; const int num_consumers = 2; std::vector<std::thread> producers; std::vector<std::thread> consumers; for (int i = 0; i < num_producers; ++i) { producers.emplace_back(producer, i); } for (int i = 0; i < num_consumers; ++i) { consumers.emplace_back(consumer, i); } for (auto& p : producers) { p.join(); } for (auto& c : consumers) { c.join(); } return 0; }
解释
- 生产者:每个生产者线程尝试将一个整数添加到队列中。如果队列已满,则等待条件变量
cond_var
被通知(即队列有空位)。 - 消费者:每个消费者线程从队列中取出一个整数。如果队列为空,则等待条件变量
cond_var
被通知(即队列中有数据)。 - 互斥锁:使用
std::mutex
来保护对共享队列的操作,确保线程安全。 - 条件变量:使用
std::condition_variable
来协调生产者和消费者之间的同步。
示例 2:简单的任务调度器
以下是一个简单的任务调度器的例子,展示如何使用 std::condition_variable
来管理任务队列。
#include <iostream> #include <queue> #include <thread> #include <mutex> #include <condition_variable> #include <functional> #include <vector> std::queue<std::function<void()>> task_queue; std::mutex mutex; std::condition_variable cond_var; bool done = false; void worker(int id) { while (true) { std::function<void()> task; { std::unique_lock<std::mutex> lock(mutex); cond_var.wait(lock, [] { return !task_queue.empty() || done; }); if (done && task_queue.empty()) { break; } task = task_queue.front(); task_queue.pop(); } task(); // 执行任务 std::cout << "Worker " << id << " executed a task." << std::endl; } } void submit_task(const std::function<void()>& task) { { std::lock_guard<std::mutex> lock(mutex); task_queue.push(task); } cond_var.notify_one(); // 通知工作线程 } int main() { const int num_workers = 3; std::vector<std::thread> workers; for (int i = 0; i < num_workers; ++i) { workers.emplace_back(worker, i); } // 提交一些任务 for (int i = 0; i < 10; ++i) { submit_task([i] { std::cout << "Task " << i << " is being processed." << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(100)); }); } // 等待所有任务完成 std::this_thread::sleep_for(std::chrono::seconds(2)); // 设置结束标志并通知所有工作线程 { std::lock_guard<std::mutex> lock(mutex); done = true; } cond_var.notify_all(); for (auto& w : workers) { w.join(); } return 0; }
解释
- 任务队列:
task_queue
存储需要执行的任务。 - 工作线程:每个工作线程从任务队列中取出一个任务并执行。如果没有任务可执行,则等待条件变量
cond_var
被通知。 - 提交任务:
submit_task
函数将新任务添加到任务队列中,并通知一个等待的工作线程。 - 结束标志:
done
标志用于指示所有任务是否已完成,当设置为true
并通知所有工作线程时,它们会检查队列是否为空并退出循环。
注意事项
-
死锁:确保在调用
wait
方法之前已经持有互斥锁,并且在条件满足后正确解锁。否则可能导致死锁。 -
虚假唤醒:
wait
方法可能会由于虚假唤醒而返回,即使没有实际的通知。因此,通常需要结合谓词(predicate)来检查条件是否真正满足。 -
性能考虑:频繁的锁争用会影响性能。尽量减少锁的持有时间和频率,以提高并发性能。
-
内存顺序:
std::condition_variable
内部已经处理了必要的内存顺序问题,因此用户不需要额外关注这一点。
总结
std::condition_variable
提供了一种高效的线程间同步机制,适用于需要等待某些条件满足的场景。- 它通常与
std::mutex
结合使用,以确保线程安全。 - 通过
wait
、notify_one
和notify_all
方法,可以实现灵活的线程同步逻辑。
如果你有更多具体的需求或问题,请告诉我!我会尽力提供帮助。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
2018-01-10 研究|数据预处理|归一化 (标准化)
2017-01-10 Git Submodule管理项目子模块
2017-01-10 git submodule的使用
2017-01-10 More C++ Idioms
2017-01-10 C++11
2017-01-10 C++11常用特性的使用经验总结(转载)