“行动中的并发”原子操作示例的正确性

问题描述 投票:3回答:2

我已经研究了'并发行动'的位置已经有一段时间了,我在理解下面的代码示例时遇到了问题(代码清单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
2个回答
1
投票

我理解您的担忧,但书中的代码很好。默认情况下,每个带有atomics的操作都是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
投票

从Effective Modern C ++,Item 40,它说“std :: atomics对如何重新排序代码施加限制,并且一个这样的限制是在源代码中,在std :: atom变量写入之前没有代码可能需要之后放置。“注意事项在使用顺序一致性时是正确的,这是一个公平的假设。

© www.soinside.com 2019 - 2024. All rights reserved.