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

C11内存模型下原子操作与数据写入顺序的正确性问询

问题描述

我拥有两个atomic int类型变量及其他数据,要求代码执行顺序严格遵循以下步骤:

  1. 写入第一个atomic int
  2. 写入其他数据
  3. 写入第二个atomic int
    且步骤2绝不能被重排至步骤1之前。请问下述代码是否正确?若不正确,原因是什么?
_Atomic uint32_t begin_id;
_Atomic uint32_t end_id;
int some_other_data = 0;

void bla()
{
    const uint32_t id = ...;
    atomic_store_explicit(&begin_id, id, memory_order_release);

    // ...writing some other data...
    some_other_data = f(x);

    atomic_store_explicit(&end_id, id, memory_order_release);
}

我对内存序的使用存在疑问。

--- 编辑补充 ---
最终我将代码修改为实现seqlock,主要区别在于读取时不使用循环(仅尝试一次),且使用atomic_thread_fence(memory_order_acquire)确保数据仅在序列首次递增后写入,同时无需自旋锁。


代码正确性分析

这段代码无法保证步骤2不会被重排到步骤1之前,核心原因是对memory_order_release的语义理解有误:

  • memory_order_release的作用是:当前线程中,该原子操作之前的所有内存写操作,不能被重排到这个原子操作之后;但它完全不限制该原子操作之后的写操作被重排到它之前。
  • 你给begin_id的写入加了memory_order_release,只能确保begin_id的写入不会被挪到它之前的代码后面,却拦不住编译器或CPU把后面的some_other_data = f(x)重排到begin_id写入的前面,直接违反了你的顺序要求。

要满足“步骤2绝不能在步骤1之前”的要求,有两种轻量可行的调整方式:

  1. 在begin_id写入后添加写屏障:
    atomic_store_explicit(&begin_id, id, memory_order_relaxed);
    atomic_thread_fence(memory_order_release);
    some_other_data = f(x);
    
    屏障会强制所有屏障前的写操作完成后,才执行屏障后的操作,彻底阻止重排。
  2. 把begin_id写入的内存序改为memory_order_seq_cst,但这种全序内存序会带来额外性能开销,非必要不推荐。

关于简化版seqlock的注意事项

你修改后的seqlock去掉了循环重试、用atomic_thread_fence(memory_order_acquire)替代自旋锁,需要明确适用场景:

  • 放弃循环重试意味着,一旦读取时刚好遇上写入操作,必然会读到不一致的数据,只适合写入频率极低、对数据一致性容忍度较高的场景;
  • atomic_thread_fence(memory_order_acquire)能确保屏障后读取数据时,序列值的递增操作已完成,避免读到未完全写入的脏数据,但解决不了“读取中途写入导致数据撕裂”的问题——这也是标准seqlock保留循环重试的核心原因。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 20:12:40