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

写入相同值的CAS操作能否保证逻辑正确?能否替换为Load操作?

原子版本控制实现仅保留最新请求结果的逻辑分析

我们有这样一个场景:每次点击按钮都会发起网络请求,但只需要保留并使用最新请求的结果。为实现这个逻辑,我用原子对象记录请求版本,只有当结果对应的版本和当前版本一致时,才会使用该结果。

std::atomic<int> version = {0};

// 按钮点击处理函数
void OnClick(){
 auto v = version.fetch_add(1, std::memory_order::acq_rel); // #1
 std::thread([v](){
     int current = v+1;
     auto r = network_request(current ); // #2
     if(version.compare_exchange_strong(current, current, std::memory_order::release, std::memory_order::relaxed)){ // #3
       use_req(r);
     }
 });
}

假设有两次按钮点击事件A和B,A发生在B之前:

  • 如果A中的#3操作晚于B中的#1操作,A的请求结果会被忽略;
  • 如果A中的#3操作早于B中的#1操作,A的CAS操作会成功,结果会被使用,随后B中的#1会读取A中#3写入的值,这也保证了A的网络请求happen-before于B的网络请求(假设use_req是线程安全的)。

核心问题:
那么,写入相同值的CAS操作能否保证逻辑正确?这个CAS操作能不能替换成单纯的Load操作?


更新

我认为在检查网络请求期间版本是否更新时,纯Load操作和CAS操作存在本质差异:

使用CAS操作的情况

当#3使用CAS时,如果A的网络请求返回前触发了B,且B的fetch_add读取了A的#1产生的修改,那么A的#3必定会失败。这符合C++标准中关于原子读改写操作的规定:

原子读改写操作必须读取该操作的写入动作之前,修改顺序中的最后一个值。

这里的修改顺序必然是{A的fetch_add, B的fetch_add}。如果A的CAS操作成功,那作为读改写操作,它就违反了上述规定,所以这种场景下A的条件检查一定能检测到版本变化。

使用纯Load操作的情况

如果#3换成纯Load操作,哪怕B的#1已经读取了A的#1产生的修改,根据C++标准中关于数据竞争的规定:

如果原子对象M上的副作用X happen-before于M的值计算B,那么B的取值要么来自X,要么来自修改顺序中X之后的某个副作用Y。

此时修改顺序仍然是{A的fetch_add, B的fetch_add},但A的#3可能读取到A的#1产生的修改X(因为存在happen-before关系),也可能读取到X之后的B的fetch_add(即Y)。换句话说,纯Load操作无法保证一定能检测到这种场景下的版本变化。


这正是纯Load操作和CAS操作在版本变化检查中的核心差异:CAS作为原子读改写操作,会强制读取修改顺序中最新的值,确保不会错过后续的版本更新;而纯Load操作仅保证读取的值在修改顺序中不早于某个点,但可能读取到旧值,导致错误地认为版本没有更新,进而使用过期的请求结果。

所以写入相同值的CAS操作是逻辑正确的,它不能被纯Load操作替换。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 16:05:56