【发布时间】:2018-12-29 13:13:30
【问题描述】:
我一直在研究“行动中的并发性”立场,但我在理解以下代码示例(清单 5.2)时遇到了问题:
#include <vector>
#include <atomic>
#include <iostream>
std::vector<int> data;
std::atomic<bool> data_ready(false);
void reader_thread()
{
while(!data_ready.load())
{
std::this_thread::sleep(std::milliseconds(1));
}
std::cout<<”The answer=”<<data[0]<<”\n”;
}
void writer_thread()
{
data.push_back(42); //write of data
data_ready=true; //write to data_ready flag
}
本书解释:
(...) 数据的写入发生在写入 data_ready 之前 标志(...)
我担心的是这句话不包括乱序执行。据我了解,当至少两条指令没有依赖的操作数时,可能会发生乱序执行。考虑到这一点:
data_ready=true
不需要任何东西
data.push_back(42)
被执行。因此,不能保证:
数据的写入发生在写入 data_ready 标志之前
我的理解是否正确,或者我不理解乱序执行中的某些内容导致对给定示例的误解?
编辑
感谢您的回答,这很有帮助。我的误解是因为不知道原子类型不仅可以防止部分地转换变量,而且还可以充当内存屏障。
例如,以下代码可能会被编译器或处理器以多种组合重新排序:
d=0;
b=5;
a=10
c=1;
产生以下顺序(多种可能性之一):
b=5;
a=10
c=1;
d=0;
这不是单线程代码的问题,因为没有一个表达式依赖于其他的操作数,但是在多线程应用程序上可能会导致未定义的行为。例如以下代码(初始值:x=0 和 y=0):
Thread 1: Thread 2:
x=10; while(y!=15);
y=15; assert(x==10);
如果没有编译器重新排序代码或处理器重新排序执行,我们可以说:“由于赋值 y=15 总是发生在赋值 x=10 之后,而断言发生在 while 循环之后,断言永远不会失败”但事实并非如此。实际执行顺序可能如下(多种可能组合之一):
Thread 1: Thread 2:
x=10; (4) while(y!=15); (3)
y=15; (1) assert(x==10); (2)
默认情况下,原子变量可确保顺序一致。如果上面示例中的 y 是原子的,并且带有 memory_order_seq_cst 默认参数,则以下句子为真:
- 之前在线程 1 (x=10) 中发生的事情在线程 2 中也像之前一样可见。
- 在线程 2 中 while(y!=15) 之后发生的事情在线程 1 中也可见
因此它的断言永远不会失败。
一些可能有助于理解的来源:
【问题讨论】:
标签: c++ multithreading atomic