2013-11-28 112 views
3

我有一個std::map,其中的密鑰與thread_num相同。每個線程都寫入該值,這裏是std::vector。因此保證每個線程僅在「他的」std::vector上運行。OpenMP:同時寫入std :: map

實施例:

#include <iostream> 
#include <omp.h> 
#include <map> 
#include <vector> 


int main(void) { 

    std::map<unsigned int, std::vector<unsigned int> > M; 

// initialize map using first touch policy: 
#pragma omp parallel for schedule(static,1) 
    for (int i=0; i<omp_get_num_procs(); ++i) { 
#pragma omp critical 
     M[omp_get_thread_num()] = std::vector<unsigned int>(); 
    } 

// do some parallel operations: 
#pragma omp parallel for schedule(static) 
    for (int i=0; i<100; ++i) { 
     M[omp_get_thread_num()].push_back(i); 
    } 

// disp the content: 
    for (auto it_key : M) { 
     std::cout << "thread " << it_key.first << " : {"; 
     for (auto it_vec : it_key.second) { 
      std::cout << it_vec << " "; 
     } 
     std::cout << "\b \b}" << std::endl; 
    } 

    return 0; 
} 

輸出看起來如需要的話。問題是上述代碼是否合法?所以我可以得出結論,我可以並行操作std::map,只要我能保證只有一個線程在特定鍵的輔助數據上運行?

+0

我不明白你的第一個循環,沒有意義的並行運行它,因爲地圖會按順序存儲所有內容。除此之外,你的第二回合看起來合法。 – SirGuy

+0

@GuyGreer好吧,第一個循環可能是不必要的複雜。這個想法是,在矢量上運行的線程應該自己創建線程(ccNUMA局部性,第一次觸摸)。重要的是,密鑰必須事先初始化。 –

回答

5

你的第二個循環看起來很好,因爲你並沒有修改地圖本身,而只是在鍵值下。 從

http://www.cplusplus.com/reference/map/map/operator[]/

數據競爭 容器被訪問,並且可能修改。 該函數訪問一個元素並返回一個可用於修改其映射值的引用。 同時訪問其他元素是安全的。 如果函數插入一個新元素,則同時在容器中迭代範圍是不安全的。

第一個循環最好不要同時執行,因爲它的工作量是最小的 - 在一個線程中執行,之後執行syn_threads。現在的方式似乎不符合標準,只是碰巧在您的配置上工作。

編輯

實際上,由於在第一循環中,您不是在訪問其他元素只是插入新的,它是由標準的安全也是如此,雖然沒有同時這樣做的好處。

+0

@ Ilya Kobelevskiy:謝謝。關鍵問題是映射的密鑰是不同時初始化的(或者使用critical),因爲密鑰會在首次使用時自動初始化。我在並行區域進行了初始化,因爲分配的內存映射到首先觸及它的內核的域(ccNUMA)。但的確,沒有必要這樣做。 –

+0

@the_ducky對,我忽略了,由於omp的關鍵,第一個循環不是併發的。即使沒有基於映射文檔的參考,它也應該工作,因爲不能訪問該循環中涉及的其他元素/迭代。 –

+1

@ Ilya Kobelevskiy:不,它在忽略omp關鍵時不起作用。初始化密鑰的非併發是絕對必要的,因爲否則多個線程可能會嘗試同時插入到映射中。導致未定義的行爲和段錯誤。 –