You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

原子存储操作前如何防止写重排?线程同步内存序选型问题

问题场景与疑问

场景代码

响应事件启动耗时异步线程,但需保证同一时间仅启动一个线程,代码如下:

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

选型原因

  1. 阻止重排风险:std::memory_order_release在op3处会强制编译器和CPU不能将其后的线程构造/启动操作重排到该存储之前,确保只有标记in_progress为true后,新线程才会被创建,完全解决你担心的重排问题。
  2. 保证线程间可见性:当foo通过release存储false时,后续调用bar的线程用acquire加载in_progress,能确保看到foo中密集型任务全部完成后的状态,避免因旧值读取导致的重复启动线程问题。
  3. 无需额外同步原语: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 15:13:20