2012-03-27 127 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的效果...