原子存储操作前如何防止写重排?线程同步内存序选型问题
问题场景与疑问
场景代码
响应事件启动耗时异步线程,但需保证同一时间仅启动一个线程,代码如下:
void foo() { // 执行密集型任务 in_progress.store(false, /*???*/); // op 1 } void bar() { if(in_progress.load(/*???*/)) // op 2 return in_progress.store(true, /*???*/); // op 3 // 调用线程运行事件循环,t完成后自行结束 std::thread t(foo); t.detach(); }
核心疑问
in_progress的三个原子操作应选用何种内存序?最初考虑使用std::memory_order_relaxed,但担心线程对象t的构造写操作会被重排至op3之前。由于无法将op3移至t构造之后(失去防护作用),需找到能防止写操作重排至原子存储之前的内存序,不确定原子操作是否足够,还是需要信号量等其他同步原语。
解决方案与内存序选择
内存序的正确配置
三个原子操作的内存序应按如下设置:
- op1(
foo中的store(false)):std::memory_order_release - op2(
bar中的load()):std::memory_order_acquire - op3(
bar中的store(true)):std::memory_order_release
选型原因
- 阻止重排风险:
std::memory_order_release在op3处会强制编译器和CPU不能将其后的线程构造/启动操作重排到该存储之前,确保只有标记in_progress为true后,新线程才会被创建,完全解决你担心的重排问题。 - 保证线程间可见性:当
foo通过release存储false时,后续调用bar的线程用acquire加载in_progress,能确保看到foo中密集型任务全部完成后的状态,避免因旧值读取导致的重复启动线程问题。 - 无需额外同步原语:
acquire-release的配对语义已经足够提供所需的同步保障,不需要信号量或其他复杂同步工具,性能开销也比最严格的seq_cst更低。
修正后的完整代码
#include <atomic> #include <thread> std::atomic<bool> in_progress{false}; void foo() { // 执行密集型任务 in_progress.store(false, std::memory_order_release); // op1 } void bar() { if(in_progress.load(std::memory_order_acquire)) // op2 return; in_progress.store(true, std::memory_order_release); // op3 std::thread t(foo); t.detach(); }
补充说明
如果需要全局严格的操作顺序一致性,也可以替换为std::memory_order_seq_cst,但它的同步开销略高,本场景下acquire-release完全满足需求。另外要确保in_progress是全局/静态的原子变量,保证所有bar和foo调用访问的是同一个实例。
内容的提问来源于stack exchange,提问作者Pacopenguin
相关产品推荐
相关产品推荐

