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

纯Load+Store能否模拟RMW操作?原子操作顺序相关问询

C++原子操作的修改顺序与操作保证问题

在C++程序中,若对某原子对象的所有操作均为**RMW(读-改-写)**操作,根据[atomic.order] p10规则,可保证该原子对象修改顺序中的某一修改仅会被一个RMW操作读取,基于此可构建临界区。

示例代码:

#include <iostream>
#include <thread>
#include <atomic>
int main(){
  std::atomic<int> state = {0};
  int protect_v = 0;
  std::thread t1([&](){
     int expected = 0;
     if(state.compare_exchange_strong(expected,1,std::memory_order::release,std::memory_order::acquire)){
          protect_v  = 2;
       }else{
          std::cout<< "t1 read: "<<protect_v  <<"\n";
       }
  });
  std::thread t2([&](){
     int expected = 0;
     if(state.compare_exchange_strong(expected,1,std::memory_order::release,std::memory_order::acquire)){
          protect_v  = 3;
       }else{
         std::cout<< "t2 read: "<<protect_v  <<"\n";
       }
  });
  t1.join();
  t2.join();
}

但若将上述程序中的任一RMW操作替换为Load操作后紧跟Store操作,该保证是否依然成立?在此情况下,若Load读取到0且Store写入1,即便这两个操作间无干扰,另一RMW操作仍可能读取到0,反之亦然,这是否会打破原有保证?


Q1:若要让单个原子对象修改顺序中的某一修改仅被一个操作读取,是否必须对该对象的所有操作均为RMW操作,以确保它们竞争读取该值?

是的,必须所有操作都是RMW操作才能保证这一点。

RMW操作是原子性的整体操作——读取当前值、计算新值、写入新值的过程不可分割。当多个RMW操作竞争同一个原子对象时,每个RMW操作要么成功修改对象(读取到的是修改顺序中前一个未被其他RMW操作“抢占”的值),要么读取到其他RMW操作已经写入的新值,不会出现两个操作同时读取到同一个旧值并都进行写入的情况。

如果混合使用非RMW操作(比如单独的Load+Store),Load和Store是两个独立的原子操作,中间存在时间窗口:线程A执行Load读到0后,线程B的RMW操作可能先完成写入1,此时线程A再执行Store写入1,就会出现两个操作(线程A的Load、线程B的RMW)都读取到了初始的0值,打破了“一个修改仅被一个操作读取”的保证。


Q2:给定如下代码:

std::atomic<bool> flag = false;
std::atomic<int> state = 0;
// thread 1:
if(state.load(std::memory_order::seq_cst)==0){
   state.store(1,std::memory_order::seq_cst);
   flag.store(true,std::memory_order::seq_cst);
}
// thread 2:
while(!flag.load(std::memory_order::relaxed)){}
state.exchange(1,std::memory_order::seq_cst);

其中的RMW操作exchange是否仍能读取到值0?

不可能读取到0。

线程2会一直等待flag变为true,而flag.store(true)是在thread1执行完state.store(1, seq_cst)之后才执行的。由于所有state的操作都是seq_cst顺序,thread1的state.store(1)在全局一致性顺序中必然先于thread2的state.exchange(1)。因此thread2的exchange操作只能读取到1,而不可能是初始的0。


Q3:Load与exchange在单一全序中如何排序?

首先明确两个标准条款的含义:

[atomics.order] p3规定:某原子对象M上的原子操作A在一致性顺序上先于M上的另一原子操作B,当且仅当:
[...]
A与B并非同一原子读-改-写操作,且存在M的某原子修改X,使得A读取X存储的值,且X在M的修改顺序中先于B,或者[...]

[atomics.types.operations] p3规定:state的初始化并非原子修改:效果:用期望的值初始化对象。初始化并非原子操作([intro.multithread])。

对于原子对象的初始值0:虽然初始化不是原子修改,但该初始值会被视为修改顺序中的“起始状态”。

回到Load和exchange的排序逻辑:

  1. 如果thread1的state.load(seq_cst)读取到初始值0,那么这个Load操作在一致性顺序中先于thread1后续的state.store(1),同时也先于thread2的state.exchange(1)——因为thread2的exchange读取的是thread1 store的1,而thread1的store在修改顺序中先于exchange的修改。
  2. 从全局seq_cst全序来看,thread1的Load、thread1的Store、thread2的Exchange会按照符合happens-before关系的顺序排列(比如Load → Store → Exchange),此时Load先于Exchange。

简言之:Load操作如果读取的是某个修改(或初始状态)的值,且该修改在修改顺序中先于exchange操作的修改,那么Load在一致性顺序中就先于exchange。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 10:33:12