簡體   English   中英

使用std :: unordered_map進行數據競爭,盡管使用互斥鎖進行鎖定插入

[英]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_lockstd :: 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM