简体   繁体   English

在执行C ++并发的代码中,memory_order_relaxed无法按预期工作

[英]memory_order_relaxed not work as expected in code from C++ concurrency in action

Before asking this question I tried set my VM cores to more than 1(I set it to 2), according to suggestion from this question , this solution worked for the author, but not for me. 在问这个问题之前,根据这个问题的建议,我尝试将我的VM内核设置为1个以上(我将其设置为2个),该解决方案对作者有效,但不适用于我。

The book C++ Concurrency in Action gives an example in Listing 5.5 《 C ++并发操作》一书给出了清单5.5中的示例。

#include <atomic>
#include <thread>
#include <assert.h>

std::atomic<bool> x,y;
std::atomic<int> z;

void write_x_then_y()
{
  x.store(true,std::memory_order_relaxed);  // 1
  y.store(true,std::memory_order_relaxed);  // 2
}
void read_y_then_x()
{
  while(!y.load(std::memory_order_relaxed));  // 3
  if(x.load(std::memory_order_relaxed))  // 4
    ++z;
}
int main()
{
    for(int i = 0; i < 1000; i++){
      // this loop is addeed by me in order to try 1000 times to see whether the assertion will fail
      x=false;
      y=false;
      z=0;
      std::thread a(write_x_then_y);
      std::thread b(read_y_then_x);
      a.join();
      b.join();
      assert(z.load()!=0);  // 5
    }
}

The book says the assertion assert(z.load()!=0) may fail. 这本书说断言assert(z.load()!=0)可能会失败。 However, I can't fail this assertion when testing. 但是,在测试时我不能使这个断言失败。

I tested the program by 64-bit Ubuntu(under VMWare), g++ -g -o test --std=c++17 test.cpp -lpthread , I tested under both gcc 5.4.0 and gcc 7.2.0. 我通过64位Ubuntu(在VMWare下), g++ -g -o test --std=c++17 test.cpp -lpthread测试了该程序,同时在gcc 5.4.0和gcc 7.2.0下进行了测试。

I also tested the program by 64-bit Windows 10(not in a VM), Visual studio 2015. 我还通过64位Windows 10(不在VM中),Visual Studio 2015测试了该程序。

Meanwhile, by searching the internet, I saw some people say this is because of the x86 architecture, however, I remember in x86, Loads may be reordered with older stores to different locations, so I think there can be a order 2-3-4-1 which can fail this assertion. 同时,通过搜索互联网,我看到有人说这是因为x86体系结构,但是,我记得在x86中,旧商店的负载可能会重新排序到不同的位置,所以我认为可以订购2-3-4-1可能会使此断言失败。

The book is correct, the assertion may fail, just not on x86 as you suspected. 这本书是正确的,断言可能会失败,只是您怀疑的不是在x86上。

To be clear, atomics make the specific guarantee that no data races will occur. 需要明确的是, 原子能特别保证不会发生数据 用。 It doesn't by itself guarantee absence of race conditions . 它本身并不能保证没有比赛条件

std::memory_order_relaxed is the loosest of all memory orders. std::memory_order_relaxed是所有内存命令中最宽松的。 It is the baseline of everything. 这是一切的基线。 In practice, it prevents compiler optimizations around the variables. 实际上,它防止围绕变量进行编译器优化

atomic<bool> x, y;
x.store(true,std::memory_order_relaxed);
y.store(true,std::memory_order_relaxed);

// is similar to...
volatile bool x, y;
x = true;
asm volatile("" ::: "memory");  // Tells compiler to stop optimizing here,
                                // but issues no instructions
y = true;

However, the CPU might itself decide to reorder the writes or the cache system might decide to publish the write to x some time later. 但是,CPU本身可能决定对写入进行重新排序,或者缓存系统可能会在一段时间后决定将写入发布到x This ultimately leads to the assertion fail. 最终导致断言失败。

In terms of the language, this is due to the writes have no happens before relation with the reads and therefore have no guarantees on their results. 就语言而言,这是由于写入与读取相关之前没有发生 ,因此不能保证其结果。

Finally, the reason you see no failed assertions on a x86 machine is because it inherently has acquire and release semantics on its reads and writes. 最后,您在x86机器上看不到失败的断言的原因是因为它固有地在其读取和写入过程中具有获取和释放语义

// On x86, effectively
void write_x_then_y()
{
    x.store(true,std::memory_order_release);  // 1
    y.store(true,std::memory_order_release);  // 2
}
void read_y_then_x()
{
    while(!y.load(std::memory_order_acquire));  // 3
    if(x.load(std::memory_order_acquire))  // 4
        ++z;
}

Which is to say, if y is determined to be true at // 3 , what happened before //2 is guaranteed to be visible after // 3 . 也就是说,如果确定y// 3处为true ,则保证//2之前发生的事情在// 3之后是可见的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM