2012-03-27 128 views
2

我在玩C++ Concurrency in Action中的一个例子,它使用std::memory_order_relaxed来从5个不同的线程读取和写入3个原子变量。示例程序如下:为什么memory_order_relaxed和memory_order_seq_cst没有区别?

#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程序给出完全相同的输出!

我本来会期望来自该程序的两个版本的不同输出。为什么std::memory_order_relaxedstd::memory_order_seq_cst的输出之间没有什么区别?

为什么std::memory_order_relaxed对于程序的每次运行都会产生完全相同的结果?

我使用: - 32位的Ubuntu安装为一个虚拟机(下VMWare的) - - 英特尔四核处理器 GCC 4.6.1-9

的代码被编译: 克++ - std = C++ 0x -g mem-order-relaxed.cpp -o relaxed -pthread

注意-pthread是必需的,否则会报告以下错误: 在抛出std :: system_error' what():不允许操作

由于缺乏对GCC的支持,或者由于在VMWare下运行,我看到的行为是什么?

+2

您已将多少个处理器内核分配给VM? – 2012-03-27 09:10:14

+0

@Michael Burr - 这就解决了--Vmware被设置为1核心,现在我已经将它设置为4核心,我得到了预期的结果。不得不承认我是使用VMWare的新手 - 我习惯于将Linux作为本机操作系统。如果你发布你的建议,我会接受它作为答案。干杯。 – mark 2012-03-27 09:41:05

回答

5

您已将多少个处理器内核分配给VM?分配多个核心给虚拟机,让它利用并发性。

1

您使用yield会导致程序的性能更依赖于您的平台的调度程序。

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

0

许多版本的GCC忽略您提供的内存排序,并用顺序一致性替换它。你可以在头文件中看到这个。希望他们最终会有更好的实施?您可以通过使用CDSChecker轻松实现与seq_cst的效果...