为什么memory_order_relaxed和memory_order_seq_cst没有区别?

时间:2012-03-27 08:56:51

标签: c++ concurrency c++11 atomic

我正在使用C ++ Concurrency in Action中的一个示例,它使用std::memory_order_relaxed来读取和编写来自5个不同线程的3个原子变量。示例程序如下:

#include <thread>
#include <atomic>
#include <iostream>

std::atomic<int> x(0);
std::atomic<int> y(0);
std::atomic<int> z(0);
std::atomic<bool> go(false);

const unsigned int loop_count = 10;

struct read_values
{
   int x;
   int y;
   int z;
};

read_values values1[loop_count];
read_values values2[loop_count];
read_values values3[loop_count];
read_values values4[loop_count];
read_values values5[loop_count];

void increment( std::atomic<int>* v, read_values* values )
{
    while (!go)
       std::this_thread::yield();

    for (unsigned i=0;i<loop_count;++i)
    {
       values[i].x=x.load( std::memory_order_relaxed );
       values[i].y=y.load( std::memory_order_relaxed );
       values[i].z=z.load( std::memory_order_relaxed );
       v->store( i+1, std::memory_order_relaxed );
       std::this_thread::yield();
    }
}

void read_vals( read_values* values )
{

   while (!go)
      std::this_thread::yield();

   for (unsigned i=0;i<loop_count;++i)
   {
      values[i].x=x.load( std::memory_order_relaxed );
      values[i].y=y.load( std::memory_order_relaxed );
      values[i].z=z.load( std::memory_order_relaxed );
      std::this_thread::yield();
   }
}

void print( read_values* values )
{
   for (unsigned i=0;i<loop_count;++i)
   {
      if (i)
         std::cout << ",";
      std::cout << "(" << values[i].x <<","
                       << values[i].y <<","
                       << values[i].z <<")";
   }
   std::cout << std::endl;
}

int main()
{
   std::thread t1( increment, &x, values1);
   std::thread t2( increment, &y, values2);
   std::thread t3( increment, &z, values3);
   std::thread t4( read_vals, values4);
   std::thread t5( read_vals, values5);

   go = true;

   t5.join();
   t4.join();
   t3.join();
   t2.join();
   t1.join();

   print( values1 );
   print( values2 );
   print( values3 );
   print( values4 );
   print( values5 );

   return 0;
}

每次运行程序时,我都会得到完全相同的输出:

(0,10,10),(1,10,10),(2,10,10),(3,10,10),(4,10,10),(5,10,10),(6,10,10),(7,10,10),(8,10,10),(9,10,10)
(0,0,1),(0,1,2),(0,2,3),(0,3,4),(0,4,5),(0,5,6),(0,6,7),(0,7,8),(0,8,9),(0,9,10)
(0,0,0),(0,1,1),(0,2,2),(0,3,3),(0,4,4),(0,5,5),(0,6,6),(0,7,7),(0,8,8),(0,9,9)
(0,0,0),(0,0,0),(0,0,0),(0,0,0),(0,0,0),(0,0,0),(0,0,0),(0,0,0),(0,0,0),(0,0,0)
(0,0,0),(0,0,0),(0,0,0),(0,0,0),(0,0,0),(0,0,0),(0,0,0),(0,0,0),(0,0,0),(0,0,0)

如果我从std::memory_order_relaxed更改为std::memory_order_seq_cst,该程序会提供完全相同的输出!

我原本期望该程序的2个版本有不同的输出。为什么std::memory_order_relaxedstd::memory_order_seq_cst的输出没有区别?

为什么std::memory_order_relaxed总是为程序的每次运行产生完全相同的结果?

我正在使用:   - 作为虚拟机安装的32位Ubuntu(在VMWare下)   - INtel四核处理器   - GCC 4.6.1-9

代码编译为:   g ++ --std = c ++ 0x -g mem-order-relaxed.cpp -o relaxed -pthread

请注意-pthread是必需的,否则会报告以下错误:     抛出'std :: system_error'的实例后终止调用     what():不允许操作

我看到的行为是由于缺乏对GCC的支持,还是因为在VMWare下运行?

3 个答案:

答案 0 :(得分:6)

您已为VM分配了多少个处理器核心?为VM分配多个核心,以利用并发性。

答案 1 :(得分:2)

使用yield会导致程序的性能更依赖于平台的调度程序而不是其他任何内容。

话虽这么说,memory_order_relaxed并不要求编译器重新排序原子,它只允许编译器这样做。如果编译器对使用memory_order_seq_cst获得的排序感到满意,那么它实际上可能产生完全相同的字节码!在x86上尤其如此,因为指令集已经提供了如此多的排序保证,因此达到memory_order_seq_cst并没有太大的飞跃。

答案 2 :(得分:0)

许多版本的GCC会忽略您提供的内存排序,并将其替换为顺序一致性。您可以在头文件中看到这一点。希望他们最终会有更好的实施?你可以使用CDSChecker来解决轻松与seq_cst的影响...