一段时间以来,我一直在研究“行动中的并发性”职位,在理解以下代码示例(清单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之后发生,而assert发生在while循环之后,assert永远不会失败”,但这不是事实。实际执行顺序可能如下(许多可能的组合之一):
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中也可见
结果是断言永远不会失败。
一些有助于理解的资源
答案 0 :(得分:1)
我了解您的担忧,但是本书中的代码很好。默认情况下,每个原子操作都是memory_order_seq_cst
,这意味着在其中一个线程中写入之前发生的所有事情都在其余线程中读取之前发生。您可以像这样std::memory_order
想象原子操作:
std::atomic<bool> a;
//equivalent of a = true
a.assign_and_make_changes_from_thread_visible(true);
//equvalent of a.load()
a.get_value_and_changes_from_threads();
答案 1 :(得分:1)
在有效的现代C ++项目40中,它说:“ std :: atomics对代码的重新排序施加了限制,并且这样的限制是在源代码中没有任何代码先于std :: atomic的编写变量可能随后发生。”需要注意的是,对于使用顺序一致性的情况,这是一个正确的假设。