[英]Data race with std::unordered_map, despite locking insertions with mutex
我有一個C ++ 11程序,它執行一些計算並使用std::unordered_map
來緩存那些計算的結果。 該程序使用多個線程,並使用共享的unordered_map
來存儲和共享計算結果。
基於我對unordered_map
和STL容器規范的讀取以及unordered_map線程安全性 ,似乎多個線程共享的unordered_map
可以一次處理一個線程,但一次只能處理許多讀者。
因此,我使用std::mutex
將我的insert()
調用包裝到地圖中,因此一次最多只插入一個線程。
但是,我的find()
調用沒有互斥,因為從我的閱讀來看,似乎許多線程應該能夠立即讀取。 但是,我偶爾會得到數據競賽(由TSAN檢測到),在SEGV中表現出來。 數據競爭明確指向我上面提到的insert()
和find()
調用。
當我在一個互斥鎖中包裝find()
調用時,問題就消失了。 但是,我不想序列化並發讀取,因為我試圖盡可能快地使這個程序。 (僅供參考:我正在使用gcc 5.4。)
為什么會這樣? 我對std::unordered_map
的並發保證的理解是不正確的?
你仍然需要一個mutex
讓你的讀者保持作家,但你需要一個共享 。 C++14
有一個std :: shared_timed_mutex ,你可以使用scoped lock std :: unique_lock和std :: shared_lock,如下所示:
using mutex_type = std::shared_timed_mutex;
using read_only_lock = std::shared_lock<mutex_type>;
using updatable_lock = std::unique_lock<mutex_type>;
mutex_type mtx;
std::unordered_map<int, std::string> m;
// code to update map
{
updatable_lock lock(mtx);
m[1] = "one";
}
// code to read from map
{
read_only_lock lock(mtx);
std::cout << m[1] << '\n';
}
這種方法存在一些問題。
首先, std::unordered_map
有兩個find
重載 - 一個是const
,一個不是。
我敢說我不相信find
非const版本會改變地圖,但是仍然因為編譯器從多個線程調用非const方法是一個數據競爭,一些編譯器實際上使用未定義的行為討厭的優化。
首先 - 你需要確保當多個線程調用std::unordered_map::find
他們使用const版本。 這可以通過使用const引用引用地圖然后從那里調用find
來實現。
第二,你錯過了許多線程可能在你的地圖上調用const查找的部分,但是其他線程無法在對象上調用非const方法! 我可以想象很多線程同時調用find
和一些調用insert
,導致數據競爭。 想象一下,例如, insert
使地圖的內部緩沖區重新分配,而其他一些線程迭代它以找到想要的對。
解決方案是使用C ++ 14 shared_mutex
,它具有獨占/共享鎖定模式。 當線程調用find
,它鎖定共享模式的鎖,當一個線程調用insert
它將它鎖定在獨占鎖上。
如果編譯器不支持shared_mutex
,則可以使用特定於平台的同步對象,例如Linux上的pthread_rwlock_t
和Windows上的SRWLock
。
另一種可能性是使用無鎖HashMap中,就像英特爾線程構建模塊庫提供的一個或concurrent_map
上MSVC並發運行。 實現本身使用無鎖算法,確保訪問始終是線程安全的,同時快速。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.