'Concurrency in Action' 原子操作示例的正确性
Correctness of 'Concurrency in Action' atomic operation example
我研究 'Concurency in Action' 职位有一段时间了,但我在理解以下代码示例(清单 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
}
书中解释:
(...) The write of the data happens-before the write to the data_ready
flag (...)
我担心的是句子没有涵盖乱序执行。根据我的理解,当至少两条指令没有依赖的操作数时,可能会发生乱序执行。考虑到这一点:
data_ready=true
不需要
的任何东西
data.push_back(42)
待执行。因此,不能保证:
The write of the data happens-before the write to the data_ready flag
我的理解是否正确,或者乱序执行中有什么我不理解导致对给定示例的误解?
编辑
感谢您的回答,很有帮助。我的误解是因为不知道原子类型不仅可以防止部分改变变量,而且还可以作为内存屏障。
例如,编译器或处理器可能会以多种组合对以下代码进行重新排序:
d=0;
b=5;
a=10
c=1;
产生以下顺序(许多可能性之一):
b=5;
a=10
c=1;
d=0;
这不是单线程代码的问题,因为 none 表达式依赖于其他操作数,但在多线程应用程序中可能会导致未定义的行为。例如下面的代码(初始值:x=0 和 y=0):
Thread 1: Thread 2:
x=10; while(y!=15);
y=15; assert(x==10);
如果不通过编译器重新排序代码或通过处理器重新排序执行,我们可以说:"Since assigement y=15 allways happens after assigement x=10 and assert happens after while loop the assert will never fail"但事实并非如此。真正的执行顺序可能如下(许多可能的组合之一):
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 中也可见
因此断言永远不会失败。
一些可能有助于理解的来源:
我理解你的顾虑,但是书上的代码没问题。默认情况下,每个原子操作都是 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();
来自 Effective Modern C++,第 40 项,它说 "std::atomics imposes restrictions on how code can be reordered, and one such restriction is that no code that, in the source cod, precedes a write of std::atomic variable may take place afterwards." 注意,当使用顺序一致性时这是正确的,这是一个合理的假设。
我研究 'Concurency in Action' 职位有一段时间了,但我在理解以下代码示例(清单 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
}
书中解释:
(...) The write of the data happens-before the write to the data_ready flag (...)
我担心的是句子没有涵盖乱序执行。根据我的理解,当至少两条指令没有依赖的操作数时,可能会发生乱序执行。考虑到这一点:
data_ready=true
不需要
的任何东西data.push_back(42)
待执行。因此,不能保证:
The write of the data happens-before the write to the data_ready flag
我的理解是否正确,或者乱序执行中有什么我不理解导致对给定示例的误解?
编辑
感谢您的回答,很有帮助。我的误解是因为不知道原子类型不仅可以防止部分改变变量,而且还可以作为内存屏障。
例如,编译器或处理器可能会以多种组合对以下代码进行重新排序:
d=0;
b=5;
a=10
c=1;
产生以下顺序(许多可能性之一):
b=5;
a=10
c=1;
d=0;
这不是单线程代码的问题,因为 none 表达式依赖于其他操作数,但在多线程应用程序中可能会导致未定义的行为。例如下面的代码(初始值:x=0 和 y=0):
Thread 1: Thread 2:
x=10; while(y!=15);
y=15; assert(x==10);
如果不通过编译器重新排序代码或通过处理器重新排序执行,我们可以说:"Since assigement y=15 allways happens after assigement x=10 and assert happens after while loop the assert will never fail"但事实并非如此。真正的执行顺序可能如下(许多可能的组合之一):
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 中也可见
因此断言永远不会失败。
一些可能有助于理解的来源:
我理解你的顾虑,但是书上的代码没问题。默认情况下,每个原子操作都是 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();
来自 Effective Modern C++,第 40 项,它说 "std::atomics imposes restrictions on how code can be reordered, and one such restriction is that no code that, in the source cod, precedes a write of std::atomic variable may take place afterwards." 注意,当使用顺序一致性时这是正确的,这是一个合理的假设。