移除task++的lock_guard为何导致std::condition_variable wait_for超时?
std::condition_variable 相关问题解答
问题描述
学习std::condition_variable时编写了测试代码,移除worker_thread2中task++的std::lock_guard后出现wait_for超时情况,有以下疑问:
- 如果
wait执行前线程2已完成task++并调用notify,是否会引发丢失唤醒问题? - 是否必须给
task++和cv.notify_all()加锁才能解决该问题?但看到示例中是先解锁再调用notify_all,说是为了避免等待线程再次阻塞,这里有点困惑。 wait_for的具体执行流程是什么?收到通知后是唤醒加锁再检查条件吗?
测试代码
std::mutex m; std::condition_variable cv; int task = 0; void worker_thread2() { { // std::lock_guard<std::mutex> lk(m); task++; } cv.notify_all(); LOG("notify_all"); } int main() { std::thread worker(worker_thread2); { std::unique_lock<std::mutex> lk(m); cv.wait_for(lk, std::chrono::milliseconds(5000), [&]{ if (task == 1) { LOG("1111111"); return true; } else { LOG("0000000"); return false; } }); } std::string m = "Back in main(), data = " + std::to_string(task); LOG(m); worker.join(); }
运行结果
D:\code\c++11\condition_variable\cmake-build-debug\condition_variable.exe 2023-6-8 20:37:15.208 00000002023-6-8 20:37:15.208 notify_all 2023-6-8 20:37:20.210 1111111 2023-6-8 20:37:20.212 Back in main(), data = 1 Process finished with exit code 0
问题解答
1. 丢失唤醒的问题
肯定会出现丢失唤醒的情况。
比如执行顺序是:主线程还没进入wait_for,worker线程先跑完task++并调用notify_all,之后主线程才开始等待。这时条件变量的通知已经发出去了,但主线程还没开始监听,这个通知就直接丢失了。主线程会一直等超时,到时间后再次检查条件,发现task已经是1,才退出等待——这就是你看到运行结果里隔了5秒才输出1111111的原因。
2. 加锁的必要性
- 首先,
task是多线程共享变量,task++不是原子操作,必须用锁保护,否则会出现数据竞争,属于未定义行为。 - 其次,为了避免丢失唤醒,必须保证检查条件和进入等待是原子操作。主线程检查
task状态时必须持有锁,这样worker线程就没法在主线程检查完条件、还没进入等待的间隙修改task并发通知——从根源上堵死丢失唤醒的窗口。
至于示例里先解锁再调用notify_all,是性能优化:如果持有锁的时候发通知,被唤醒的线程会立刻尝试抢锁,但锁还在当前线程手里,唤醒的线程会再次阻塞,直到当前线程解锁。先解锁再发通知,能让被唤醒的线程直接拿到锁,减少一次上下文切换。但要注意,解锁和发通知之间不能有其他修改共享变量的操作,否则可能出问题。
回到你的代码,必须给task++加上锁,这是硬性要求。
3. wait_for的执行流程
结合你代码里带谓词的版本,完整流程是:
- 传入的
unique_lock必须是已加锁状态(你的代码满足这个前提)。 - 立刻调用谓词检查条件:
- 如果谓词返回
true,直接返回,锁保持加锁状态。 - 如果返回
false,进入等待:
a. 自动解锁unique_lock。
b. 阻塞线程,等待通知或者超时。
- 如果谓词返回
- 收到通知或超时后:
a. 自动重新加锁unique_lock。
b. 再次调用谓词检查条件:- 返回
true则退出,锁保持加锁;返回false(比如超时或虚假唤醒)也退出,锁同样保持加锁。
- 返回
一句话总结:加锁→检查条件→(不满足则)解锁等待→(唤醒/超时)重新加锁→再检查条件→退出。
内容的提问来源于stack exchange,提问作者Windows_Girl
相关产品推荐
相关产品推荐

