c++ - 使用 memory_order_seq_cst 输出 10
问题描述
当我运行这个程序时,我得到的输出为 10,这对我来说似乎是不可能的。我在 x86_64 core i3 ubuntu 上运行它。
如果输出为 10,则 1 必须来自 c 或 d。
同样在线程 t[0] 中,我们将 c 赋值为 1。现在 a 为 1,因为它出现在 c=1 之前。c 等于 b,线程 1 将其设置为 1。所以当我们存储 d 时,它应该是 1,因为 a=1。
- memory_order_seq_cst 可以输出 10 吗?我尝试在第一行(变量 =1 )和第二行(printf)之间的两个线程上插入 atomic_thread_fence(seq_cst) 但它仍然不起作用。
取消注释两个栅栏都不起作用。尝试使用g++和clang++运行。两者都给出相同的结果。
#include<thread>
#include<unistd.h>
#include<cstdio>
#include<atomic>
using namespace std;
atomic<int> a,b,c,d;
void foo(){
a.store(1,memory_order_seq_cst);
// atomic_thread_fence(memory_order_seq_cst);
c.store(b,memory_order_seq_cst);
}
void bar(){
b.store(1,memory_order_seq_cst);
// atomic_thread_fence(memory_order_seq_cst);
d.store(a,memory_order_seq_cst);
}
int main(){
thread t[2];
t[0]=thread(foo); t[1]=thread(bar);
t[0].join();t[1].join();
printf("%d%d\n",c.load(memory_order_seq_cst),d.load(memory_order_seq_cst));
}
bash$ while [ true ]; do ./a.out | grep "10" ; done
10
10
10
10
解决方案
10 (c=1, d=0) 很容易解释:bar
碰巧先运行,并在foo
read之前完成b
。
thread(foo)
在不同的内核上启动线程的内核间通信的怪癖意味着即使首先在主线程中运行,也很容易发生这种情况。例如,也许一个中断到达了操作系统选择的核心foo
,延迟了它实际进入该代码1。
请记住, seq_cst 仅保证所有 seq_cst 操作存在一些总顺序,该顺序与每个线程中的先序顺序兼容。(以及由其他因素建立的任何其他发生之前的关系)。因此,以下原子操作顺序是可能的,甚至无需将a.load
2 in bar 与生成的 int 临时分开d.store
。
b.store(1,memory_order_seq_cst); // bar1. b=1
d.store(a,memory_order_seq_cst); // bar2. a.load reads 0, d=0
a.store(1,memory_order_seq_cst); // foo1
c.store(b,memory_order_seq_cst); // foo2. b.load reads 1, c=1
// final: c=1, d=0
atomic_thread_fence(seq_cst)
在任何地方都没有影响,因为您的所有操作都已经seq_cst
. 栅栏基本上只是停止对该线程操作的重新排序;它不会等待或与其他线程中的栅栏同步。
(只有看到另一个线程存储的值的加载才能创建同步。但是这样的加载不会等待另一个存储;它无法知道还有另一个存储。如果您想继续加载直到看到您期望的值,您必须编写一个自旋等待循环。)
脚注 1:由于您的所有原子变量可能都在同一个缓存行中,即使执行确实到达了两个不同内核的顶部foo
并bar
同时在两个不同的内核上执行,错误共享也可能会让来自一个线程的两个操作发生,而其他核心仍在等待获得独占所有权。尽管 seq_cst 存储足够慢(至少在 x86 上),但硬件公平的东西可能会在提交1
. 无论如何,一个线程中的两个操作都有很多方法在另一个线程之前发生并获得 10 或 01。 如果我们得到了,甚至有可能11
b=1
获得a=1
在任一加载之前。使用 seq_cst 确实会阻止硬件提前进行加载(在存储全局可见之前),所以这是很有可能的。
脚注 2:bare 的左值到右值评估a
使用重载(int)
转换,相当于a.load(seq_cst)
. from 的操作foo
可能发生在该负载和d.store
从中获取临时值的 之间。 d.store(a)
不是原子拷贝;它相当于int tmp = a;
d.store(tmp);
. 这没有必要解释你的观察。
推荐阅读
- python - 如何测试一个字符串是否部分包含在 Python 的列表中
- python - 在 for 循环中创建的 lambda 函数被覆盖
- perl - 动态 PDF 表单 CAM::PDF + PDFLib
- python-3.x - 在 python IDE (pycharm) 中显示谷歌地图或 gmaps
- asp.net-mvc - 如何让 Visual Studio 视图编辑器和预编译器使用 C# 7?
- karate - 空手道:使用来自 API1 响应的一个动态字段为 POST API2 创建 Json 请求正文
- c++ - 由于第二个参数,我的函数并不总是有效
- javascript - 从对象数组中删除重复项的更好算法是什么?
- python - RandomForestClassifier 获得前 N 个预测和相应的概率
- c++ - 从 WCHAR 转换为 const unsigned char