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

线程间响应原子自增变更:memory_order选择是否正确?

问题分析与解答

代码示例

#include <atomic>
#include <thread>

bool read_from_network() noexcept;
void write_to_network() noexcept;

int main() {
  std::atomic<size_t> work_counter;

  std::thread t1([&work_counter] {
    while (true) {
      if (read_from_network())
        work_counter.fetch_add(1, std::memory_order_release);
    }
  });

  std::thread t2([&work_counter] {
    const auto prev_val = work_counter.load(std::memory_order_acquire);
    write_to_network();
    const auto new_val = work_counter.load(std::memory_order_acquire);
    if (new_val != prev_val) {
      // get reply **after** write_to_network
    }
  });

  t1.join();
  t2.join();
}

1. 当前选择的std::memory_order是否正确?

不正确。原因如下:

  • 重排风险:t2中的write_to_network()是普通非原子操作,当前内存序没有约束它与后续new_val.load()的顺序关系。编译器或CPU可能将write_to_network()重排到new_val.load()之后,导致即使new_val检测到计数器变化,对应的t1网络读取操作也可能发生在write_to_network()之前,不符合需求(注:用户说明忽略prev_val加载后、write前的情况,但重排会导致write被延后,误判更早的网络事件为write后的)。
  • 同步方向不匹配:当前的release-acquire仅保证t1中fetch_add之前的操作(触发计数器增量的read_from_network())happens-beforet2中new_val.load()之后的代码,但无法建立t2的write_to_network()happens-beforet1对应read_from_network()的关系,无法确保计数器增量是write后的网络事件导致的。

2. 能否使用memory_order_relaxed保证程序正常工作,还是必须用memory_order_seq_cst?

  • 不能使用memory_order_relaxed:relaxed操作没有任何同步或顺序约束,编译器和CPU可以自由重排原子操作与普通操作,完全无法保证write_to_network()与计数器加载的顺序,必然会出现误判。
  • 需要使用memory_order_seq_cst:seq_cst操作会建立全局总顺序,同时普通操作不会跨越seq_cst操作重排。修改方式为:
    • 将t1中fetch_add的内存序改为std::memory_order_seq_cst
    • 将t2中两次load的内存序改为std::memory_order_seq_cst

这样可以保证:

  1. t2中的write_to_network()会被约束在两次seq_cst加载之间,不会被重排到new_val.load()之后;
  2. 全局总顺序会确保:如果new_val检测到计数器增量,那么对应的t1的fetch_add操作必然发生在t2的write_to_network()之后,满足“仅处理write后网络事件”的需求。

内容的提问来源于stack exchange,提问作者user1244932

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 05:49:55