使用C++11 condition_variable实现三线程轮流打印1-100时遇死锁问题
三个线程轮流打印1-100的死锁问题排查与解决
问题描述
我想用C++11的condition_variable实现三个线程轮流打印1到100的程序,但遇到了死锁。有时程序只输出1、2就停止,但用gdb调试时却能正常运行。我知道wait可以加谓词,但不想新增全局变量,不清楚代码问题出在哪?
原代码
#include <condition_variable> #include <iostream> #include <mutex> #include <thread> std::condition_variable cv12; std::condition_variable cv23; std::condition_variable cv31; std::mutex mtx; void Print(int i) { while (i <= 100) { if (i % 3 == 1) { { std::unique_lock<std::mutex> l(mtx); cv31.wait(l); std::cout << i << std::endl; } cv12.notify_all(); } else if (i % 3 == 2) { { std::unique_lock<std::mutex> l(mtx); cv12.wait(l); std::cout << i << std::endl; } cv23.notify_all(); } else if (i % 3 == 0) { { std::unique_lock<std::mutex> l(mtx); cv23.wait(l); std::cout << i << std::endl; } cv31.notify_all(); } i += 3; } } int main() { std::thread t1(Print, 1); std::thread t2(Print, 2); std::thread t3(Print, 3); cv31.notify_all(); t1.join(); t2.join(); t3.join(); return 0; }
运行情况
运行命令:
$ g++ -g -o tmp tmp.cpp && ./tmp
异常输出:
╰─$ ./tmp 1 2
问题分析
- 通知丢失的竞态条件:主线程创建
t1后,可能t1还未进入cv31.wait()的等待状态,主线程就调用了cv31.notify_all(),这个通知会被直接丢弃。后续t1进入等待状态后,再也不会被唤醒,导致整个程序死锁。 - 虚假唤醒:
condition_variable::wait()可能被操作系统虚假唤醒(无对应通知的唤醒),此时线程会错误地执行打印逻辑,破坏顺序,后续等待逻辑也会陷入混乱。
解决方案
线程间同步必然需要共享状态来标识执行顺序,你可以选择将共享状态封装到类中(而非全局变量),结合wait的谓词版本避免虚假唤醒和通知丢失。
方案1:封装到类中(无全局变量)
#include <condition_variable> #include <iostream> #include <mutex> #include <thread> class Printer { public: Printer() : current_turn(1) {} void Print(int start) { for (int i = start; i <= 100; i += 3) { std::unique_lock<std::mutex> l(mtx_); // 计算当前线程对应的期望轮次:1对应start=1,2对应start=2,3对应start=3(因3%3=0) int expected = start % 3; if (expected == 0) expected = 3; // 等待直到轮到当前线程执行 cv_.wait(l, [this, expected]() { return current_turn == expected; }); std::cout << i << std::endl; // 更新轮次为下一个线程 current_turn = current_turn % 3 + 1; cv_.notify_all(); } } private: std::condition_variable cv_; std::mutex mtx_; int current_turn; // 共享状态:当前轮到的线程对应的轮次(1、2、3) }; int main() { Printer p; std::thread t1(&Printer::Print, &p, 1); std::thread t2(&Printer::Print, &p, 2); std::thread t3(&Printer::Print, &p, 3); t1.join(); t2.join(); t3.join(); return 0; }
方案2:全局共享状态(简化版)
如果不介意全局变量,也可以用更简洁的实现:
#include <condition_variable> #include <iostream> #include <mutex> #include <thread> std::condition_variable cv; std::mutex mtx; int current_turn = 1; // 当前轮到的线程对应的轮次(1、2、3) void Print(int start) { for (int i = start; i <= 100; i += 3) { std::unique_lock<std::mutex> l(mtx); int expected = start % 3; if (expected == 0) expected = 3; cv.wait(l, [expected]() { return current_turn == expected; }); std::cout << i << std::endl; current_turn = current_turn % 3 + 1; cv.notify_all(); } } int main() { std::thread t1(Print, 1); std::thread t2(Print, 2); std::thread t3(Print, 3); t1.join(); t2.join(); t3.join(); return 0; }
修改说明
- 用单个
condition_variable替代三个,简化同步逻辑,所有线程等待同一个条件变量。 - 通过共享状态
current_turn明确标识当前执行的线程,结合wait的谓词版本,确保只有轮到自己时才继续执行,彻底避免虚假唤醒和通知丢失。 - 每个线程打印完成后更新轮次,通知所有等待线程,让下一个符合条件的线程执行。
内容的提问来源于stack exchange,提问作者york king
相关产品推荐
相关产品推荐

