多线程程序是否必须用mutex/conditional_variable?我的解法有漏洞吗?
线程顺序控制解法的可靠性分析
问题背景
给定三个C++函数:
void print1() { cout << "one"; cout.flush(); } void print2() { cout << "two"; cout.flush(); } void print3() { cout << "three"; cout.flush(); }
需求是创建三个线程分别执行上述函数,在无法控制CPU调度的前提下,必须保证输出顺序为onetwothree。
通常这类问题会用mutex和condition_variable解决,但有人尝试仅用普通变量实现控制,给出如下解法:
#include <functional> // function #include <iostream> #include <thread> using namespace std; class Foo { int turn; public: Foo() { turn = 1; } void first(function<void()> printFirst) { while (turn != 1) { } printFirst(); turn = 2; } void second(function<void()> printSecond) { while (turn != 2) { } printSecond(); turn = 3; } void third(function<void()> printThird) { while (turn != 3) { } printThird(); turn = 1; } }; void print1() { cout << "first"; cout.flush(); } void print2() { cout << "second"; cout.flush(); } void print3() { cout << "third"; cout.flush(); } int main() { Foo f; thread thread3([&]() { f.third(print3); }); thread thread2([&]() { f.second(print2); }); thread thread1([&]() { f.first(print1); }); thread1.join(); thread2.join(); thread3.join(); return 0; }
该解法通过Foo类中的turn变量控制线程执行顺序,测试中先启动thread3也能保证输出顺序正确,但疑问在于:这个解法是否存在漏洞?仅考虑到turn的比较与赋值是否为原子操作,但查询得知这些操作是原子的,那该解法是否绝对可靠?
解法的潜在漏洞
这个解法并非绝对可靠,存在两个关键问题:
1. 内存可见性问题
普通的int turn既不是原子类型,也没有任何同步机制(如内存屏障、互斥锁)。虽然单个的读、写操作在多数CPU架构上是原子的,但编译器或CPU可能会对变量进行优化:比如将turn的值缓存到线程的私有寄存器中,导致某个线程修改turn后,其他线程无法及时看到这个更新,会一直卡在while循环中陷入死锁。
这种问题不会在所有测试场景中出现,但在开启编译器优化(如-O2)、或者在弱内存模型的CPU架构(如ARM)上,很容易触发。
2. 忙等的性能浪费
这种while循环的忙等方式会持续占用CPU资源,相比condition_variable的阻塞等待,它不会主动释放CPU,在高负载场景下会显著降低系统整体性能。
修复方案
要让这个解法可靠且高效,有两种常见改进方式:
- 将
turn声明为std::atomic<int>,原子类型会自动保证内存可见性,确保线程间的修改能被及时感知:std::atomic<int> turn; - 改用
std::mutex配合std::condition_variable实现阻塞等待,避免忙等的性能消耗,同时保证线程间的同步。
内容的提问来源于stack exchange,提问作者Der Fänger im Roggen
相关产品推荐
相关产品推荐

