std::ccondition_variable

核心使用思路

std::condition_variable(条件变量)的核心作用是让一个/多个线程等待某个条件满足,当条件满足时,由其他线程唤醒等待的线程。
关键组合:std::condition_variable + std::mutex + 共享条件(布尔变量/计数器等)。
核心唤醒函数:

  • notify_one():唤醒一个等待的线程(随机/按调度策略)。
  • notify_all():唤醒所有等待的线程。

完整可运行示例

下面通过一个“生产者-消费者”示例,展示如何唤醒等待的任务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>

// 全局共享资源(模拟任务队列)
std::queue<int> task_queue;
// 互斥锁:保护共享资源和条件判断
std::mutex mtx;
// 条件变量:用于线程间同步
std::condition_variable cv;
// 停止标志:控制线程退出
bool stop_flag = false;

// 消费者线程(等待任务并执行)
void consumer() {
while (true) {
// 必须用unique_lock(条件变量需要解锁/加锁的灵活性)
std::unique_lock<std::mutex> lock(mtx);

// 等待条件满足:队列非空 或 停止标志为true
// 注意:必须用while循环(防止虚假唤醒)
cv.wait(lock, []() {
return !task_queue.empty() || stop_flag;
});

// 检查是否需要退出
if (stop_flag && task_queue.empty()) {
std::cout << "消费者线程退出" << std::endl;
break;
}

// 执行任务(取出队列元素)
int task = task_queue.front();
task_queue.pop();
lock.unlock(); // 解锁,让生产者可以继续生产(非必须,但能提升性能)

std::cout << "处理任务:" << task << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 模拟任务耗时
}
}

// 生产者线程(生产任务并唤醒消费者)
void producer() {
for (int i = 1; i <= 5; ++i) {
{
std::lock_guard<std::mutex> lock(mtx);
task_queue.push(i);
std::cout << "生产任务:" << i << std::endl;
} // 手动解锁,避免唤醒时还持有锁

// 唤醒一个等待的消费者线程
cv.notify_one();
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}

// 生产完毕,设置停止标志并唤醒所有消费者
{
std::lock_guard<std::mutex> lock(mtx);
stop_flag = true;
}
cv.notify_all();
}

int main() {
std::thread consumer_thread(consumer);
std::thread producer_thread(producer);

consumer_thread.join();
producer_thread.join();

return 0;
}

关键代码解释

  1. 等待逻辑(消费者)

    1
    cv.wait(lock, []() { return !task_queue.empty() || stop_flag; });
    • wait 会先解锁 mutex,让线程进入等待状态,释放CPU资源。
    • 第二个参数是条件判断谓词:只有当谓词返回 true 时,线程才会被唤醒并重新加锁;否则会继续等待(避免“虚假唤醒”)。
    • 若省略谓词,需手动用 while 检查条件:
      1
      2
      3
      while (task_queue.empty() && !stop_flag) {
      cv.wait(lock);
      }
  2. 唤醒逻辑(生产者)

    • cv.notify_one():唤醒任意一个等待在该条件变量上的线程(适合单消费者/多消费者但只需一个处理的场景)。
    • cv.notify_all():唤醒所有等待的线程(适合多消费者、需要全部处理收尾的场景)。
  3. 锁的使用

    • 操作共享资源(task_queue/stop_flag)时必须加锁(std::lock_guard/std::unique_lock)。
    • wait 要求传入 std::unique_lock(因为需要在等待时临时解锁),而 lock_guard 是不可解锁的,无法用于 wait

运行前置条件

  • 编译器支持 C++11 及以上(std::condition_variable 是 C++11 引入的)。
  • 编译命令(以g++为例):
    1
    2
    g++ -std=c++11 -pthread condition_variable_demo.cpp -o demo
    ./demo
    注意:必须加 -pthread 链接线程库,否则会报链接错误。

总结

  1. std::condition_variable 唤醒任务的核心是 notify_one()(唤醒一个)和 notify_all()(唤醒所有),需搭配 std::mutex 和条件判断使用。
  2. 等待时必须用 wait + 条件谓词(或 while 循环),避免虚假唤醒导致逻辑错误。
  3. 操作共享条件/资源时必须加锁,唤醒前建议解锁(提升性能),确保等待线程能立即获取锁。

问题1:wait() 和 wait_for() 的区别?

代码中的条件变量使用

1
2
3
4
5
6
7
8
9
10
11
12
13
int MpFileManager::workerThread()
{
std::unique_lock<std::mutex> lock(mutex);
is_run.store(true);
while (is_run.load()) {
queueCondition_.wait_for(lock, std::chrono::seconds(2), [this] {
return !is_run.load() || work_start.load(); });
if (!is_run.load())
break;

lock.lock();
}
}

wait 和 wait_for 的区别

1. 等待方式

  • wait: 无限期等待,直到条件变量被通知或被虚假唤醒
  • wait_for: 有时间限制的等待,在指定时间段内等待,超时后自动返回

2. 超时控制

  • wait: 无超时参数,可能永久阻塞
  • wait_for: 接收时间间隔参数(如代码中的 std::chrono::seconds(2)),避免无限等待

3. 实际应用

  • wait: 适用于必须等待到条件满足的场景
  • wait_for: 适用于需要定期检查或避免死锁的场景,代码中每秒检查一次条件

4. 返回时机

  • wait: 只在条件满足或被通知时返回
  • wait_for: 可能因超时、条件满足或被通知而返回