示例
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <chrono>
using namespace std;
queue<int> q;
bool is_done;
mutex mtx;
condition_variable cv;
void producer() {
for (int i = 1; i <= 20; ++i) {
this_thread::sleep_for(std::chrono::milliseconds(10));
unique_lock<std::mutex> lock(mtx);
q.push(i+100);
lock.unlock();
cv.notify_one();
}
unique_lock<std::mutex> lock(mtx);
is_done = true;
lock.unlock();
cv.notify_all();
}
void consumer(int id) {
while (true) {
unique_lock<mutex> lock(mtx);
cv.wait(lock, []{ return !q.empty() || is_done; });
if (is_done && q.empty())
break;
int item = q.front();
q.pop();
lock.unlock();
cout << "Consumer " << id << " consumed item " << item << endl;
this_thread::sleep_for(chrono::milliseconds(50));
}
}
int main() {
const int num_consumers = 3;
thread producer_thread(producer);
thread consumer_threads[num_consumers];
for (int i = 0; i < num_consumers; ++i) {
consumer_threads[i] = thread(consumer, i + 1);
}
producer_thread.join();
for (auto& th : consumer_threads) {
th.join();
}
cout << "All threads finished." << endl;
return 0;
}
producer() 函数
std::unique_lockstd::mutex lock(mtx);
• 进入临界区,把队列锁住。
• 相比 lock_guard,unique_lock 可以在后面 手动 unlock(),因此更灵活。
lock.unlock();
• 提前解锁的好处:
– 减少锁的粒度,让消费者尽早拿到锁。
– 避免在 notify_one() 时仍然持有锁(某些实现下会导致唤醒线程立即再次阻塞)。
consumer() 函数
cv.wait(lock, predicate)
• wait 会原子地释放 lock 并挂起线程;
• 被唤醒后先重新拿到锁,再检查谓词:
[]{ return !q.empty() || done; }
只要“队列里有东西”或“生产者已经结束”就继续,否则继续睡。
• 这是 防止虚假唤醒 (spurious wakeup) 的标准写法。
while (!q.empty()) { … }
• 生产者可能一次放入多个任务,或通知时队列里已有多条数据,所以用 内层 while 一次性处理干净。
• 注意:仍要在临界区内操作队列。
行退出条件
• 必须同时满足:done == true 且 q.empty(),确保所有数据都被消费完。
常见疑问 & 易错点
为什么用 unique_lock 而不是 lock_guard?
因为 lock_guard 不能中途 unlock(),而 unique_lock 可以,所以更适合配合条件变量。
如果忘记 notify_one() 会怎样?
消费者可能永远阻塞在 cv.wait(),程序卡死。
如果把 done 设成 atomic<bool> 不加锁行不行?
不行。虽然 done 本身是原子,但队列状态仍要互斥,否则会出现“看到 done=true 但队列却非空” 的竞态。
为什么消费者里还要再检查一次 q.empty()?
因为唤醒后可能有多个线程同时被唤醒(如果使用 notify_all()),或者生产者一次 push 了多条数据。
一句话总结
条件变量 + 互斥量 = 线程安全的“等待-通知”机制;
核心套路:
• 修改共享数据 → 加锁
• 修改完 → 先解锁再 notify_*
• 等待方 → while(!predicate) cv.wait(…)
相关函数
- 线程管理
类/函数 | 功能 |
---|
std::thread | 创建并管理线程 |
std::thread::join() | 等待线程结束 |
std::thread::detach() | 分离线程,使其独立运行 |
std::thread::get_id() | 获取线程 ID |
std::thread::hardware_concurrency() | 获取系统支持的并发线程数 |
- 互斥量
类/函数 | 功能 |
---|
std::mutex | 普通互斥量 |
std::recursive_mutex | 递归互斥量(可多次加锁) |
std::timed_mutex | 带超时功能的互斥量 |
std::shared_mutex (C++17) | 共享互斥量(支持多个读锁) |
- 锁管理
类/函数 | 功能 |
---|
std::lock_guard | 自动加锁/解锁(RAII) |
std::unique_lock | 可手动加锁/解锁的锁 |
std::scoped_lock (C++17) | 一次性加多个锁,避免死锁 |
- 条件变量
类/函数 | 功能 |
---|
std::condition_variable | 条件变量(用于线程同步) |
std::condition_variable::wait() | 等待条件满足 |
std::condition_variable::notify_one() | 通知一个等待的线程 |
std::condition_variable::notify_all() | 通知所有等待的线程 |
- 异步任务
类/函数 | 功能 |
---|
std::async() | 启动异步任务 |
std::future | 获取异步任务的结果 |
std::future::get() | 获取异步任务的返回值 |
std::future::wait() | 等待异步任务完成 |
std::promise | 设置异步任务的返回值 |
std::promise::set_value() | 设置异步任务的结果 |
std::promise::get_future() | 获取与承诺关联的 std::future |
- 原子操作
类/函数 | 功能 |
---|
std::atomic | 原子变量 |
std::atomic::fetch_add() | 原子加法 |
std::atomic::fetch_sub() | 原子减法 |
std::atomic::load() | 加载当前值 |
std::atomic::store() | 存储新值 |
- 线程工具
类/函数 | 功能 |
---|
std::thread_local | 声明线程局部变量 |
std::this_thread::get_id() | 获取当前线程的 ID |
std::this_thread::sleep_for() | 让当前线程休眠一段时间 |
std::this_thread::yield() | 让出当前线程的执行权 |