多线程原子 a b 为 memory_order_relaxed 打印 00

如何解决多线程原子 a b 为 memory_order_relaxed 打印 00

在下面的代码中,对 foo 中 a 的写入存储在存储缓冲区中,对 bar 中的 ra 不可见。同样,在 bar 中写入 b 对 foo 中的 rb 不可见,它们打印 00。

// g++ -O2 -pthread axbx.cpp ; while [ true ]; do ./a.out | grep "00"; done prints 00 within 1min
#include<atomic>
#include<thread>
#include<cstdio>
using namespace std;
atomic<long> a,b;
long ra,rb;
void foo(){
        a.store(1,memory_order_relaxed);
        rb=b.load(memory_order_relaxed);
}
void bar(){
        b.store(1,memory_order_relaxed);
        ra=a.load(memory_order_relaxed);
}
int main(){
  thread t[2]{ thread(foo),thread(bar)};
  t[0].join();t[1].join();
  if((ra==0) && (rb==0)) printf("00\n"); // each cpu store buffer writes not visible to other threads.
}

下面的代码与上面的代码几乎相同,只是去掉了变量 b 并且 foo 和 bar 具有相同的变量 'a' 并且返回值存储在 ra1 和 ra2 中。在这种情况下,我至少在运行 5 分钟后永远不会得到“00”。

  1. 在第二种情况下为什么不打印 00 ?怎么写到 x 没有存储在两个线程的 cpu 缓存中,然后打印 00 ?
  2. 它与 x86_64 有什么关系,但它在 arm/arm64/power 上打印 00 吗?
  3. 如果 arm/arm64/power 打印 00 ,存储在 foo 和 bar 中的 smp_mb() 会修复它吗?
// g++ -O2 -pthread axbx.cpp ; while [ true ]; do ./a.out | grep "00"; done doesn't print 00 within 5 min
#include<atomic>
#include<thread>
#include<cstdio>
using namespace std;
atomic<long> a,b;
long ra1,ra2;
void foo(){
        a.store(1,memory_order_relaxed);
        ra1=a.load(memory_order_relaxed);
}
void bar(){
        a.store(1,memory_order_relaxed);
        ra2=a.load(memory_order_relaxed);
}
int main(){
  thread t[2]{ thread(foo),thread(bar)};
  t[0].join();t[1].join();
  if((ra1==0) && (ra2==0)) printf("00\n"); // each cpu store buffer writes not visible to other threads.
}

解决方法

a.store(1,mo_relaxed) 在同一线程中排序 a.load 之前(在 foo 和 bar 中),因此两个加载都必须看到该存储结果(或另一个稍后的存储值) . 这使得任何一个负载都无法看到初始的 0。

线程总是看到它自己按程序顺序执行的操作,即使它们是在使用 mo_relaxed 的原子对象上。这基本上等同于确保存储和加载发生在 asm(按程序顺序),但没有任何额外的障碍来防止其他线程观察到运行时重新排序,like if you'd used volatile. (But don't)。乱序执行的基本原则是“不要破坏单线程代码”。


顺便说一句,您实际上是正确的,该值可以直接从 forwarded 中获得 store buffer,然后才能命中 L1d 缓存并变得全局可见。 (因为您没有使用 mo_seq_cst;在先前的 seq_cst 存储全局可见之前,不会发生 seq_cst 加载。例如,在 x86 上,它必须编译为 xchg 存储,或 mov + mfence。半相关:Globally Invisible load instructions 关于 x86 上加载结果的来源,尽管关于存储转发的一般观点适用于大多数主流 CPU,包括大多数 ARM。)

因此在实践中,负载很可能会看到自己线程存储的 1,而不是来自其他线程的 1,因为它会编译为允许存储转发到的 asm负载,并且负载紧随其后,因此它可能已经在执行和等待存储数据被转发的过程中,然后其他线程的存储在它们之间有任何窗口可见,除非在存储之间到达中断并加载。

例如,您可以通过存储 12 进行检查,看看您是否总是得到 12 或有时得到 21


您对为什么使用 2 个变量可以在您的版本中看到 00 的分析非常草率。

在下面的代码中,对 foo 中 a 的写入存储在存储缓冲区中,对 bar 中的 ra 不可见。同样,在 bar 中写入 b 对 foo 中的 rb 不可见

是的,存储缓冲区是 StoreLoad 重新排序的正常原因,并且 如果 foobar 几乎同时执行,那么是的,两个加载都可以在任一存储可以将自己提交到 L1d 缓存之前,发生并获取旧值。所以如果确实发生了,那是因为存储缓冲区。

但是存储缓冲区总是试图尽可能快地耗尽自己,并将待处理的存储提交到 L1d,在那里它们是全局可见的。这就是为什么很少真正看到 00。通常,一个内核将获得缓存行的独占所有权并在另一个内核的加载可以运行之前提交其存储。

a 的写入将对另一个线程中的负载不可见,这绝对不是真的。它可能会也可能不会发生。

(半相关:Store Load 重新排序是性能“最重要”的一种,也是阻塞成本最高的一种。例如 x86 asm always 阻塞其他类型,即程序-order + store-forwarding,所以重新排序 2 个 store 的东西只能在 x86 上的编译时发生。How does memory reordering help processors and compilers?)

,

在下面的代码中,对 foo 中 a 的写入存储在存储缓冲区中,对 bar 中的 ra 不可见。同样,在 bar 中写入 b 对 foo 中的 rb 不可见

这些都不是真的。 bar 是否看到 a 写入 foo 的值是不确定的,但并非未定义。同一内存上的所有原子操作都按照由实现确定的全局顺序执行。 bar 可以将 a 视为 0 或 1;要么是完全合法的,放松的记忆顺序对此没有任何改变。

在第二种情况下为什么不打印 00 ?为什么对 x 的写入不存储在两个线程的 cpu 缓存中,然后打印 00 ?

如前所述,同一内存上的所有原子操作都以某种全局顺序执行。此外,同一线程中同一内存上的所有操作都按程序顺序执行(即:不重新排序)。因此,在同一个线程中,您的 a.load 将检索刚刚设置为 a 的值或某个其他线程设置为 a 的值。它永远不会只是跳过该线程中的初始存储。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res