如果這是重復的,我很抱歉,但正如我搜索的那樣,我只找到不適用的解決方案:
所以我有一個哈希表,我希望多個執行緒同時讀取和寫入表。但是在以下情況下如何防止資料競爭:
寫入與另一個
執行緒寫入正在讀取的散列相同的散列的執行緒
uj5u.com熱心網友回復:
我以前回答過這個問題的變體。請閱讀我之前關于此主題的回答。
許多人曾嘗試實作執行緒安全的集合類(串列、哈希表、映射、集合、佇列等...)并失敗了。或者更糟的是,失敗了,不知道,但還是發貨了。
構建執行緒安全哈希表的一種簡單方法是從現有的哈希表實作開始,并向所有公共方法添加互斥鎖。你可以想象一個假設的實作是這樣的:
// **THIS IS BAD**
template<typename K, typename V>
class ThreadSafeMap
{
private:
std::map<K,V> _map;
std::mutex _mutex;
public:
void insert(const K& k, const V& v)
{
std::lock_guard lck(_mutex);
_map[k] = v;
}
const V& at(const K& key)
{
std::lock_guard lck(_mutex);
return _map.at(k);
}
// other methods not shown - but are essentially a repeat of locking a mutex
// before accessing the underlying data structure
};
上例中,變數實體化std::lock_guard時鎖定互斥lck鎖,當lck變數超出作用域時,lock_guard的解構式會釋放互斥鎖
并且在一定程度上,它是執行緒安全的。但是當你開始以復雜的方式使用上述資料結構時,它就會崩潰。
哈希表上的事務通常是多步操作。例如,表上的整個應用程式事務可能是查找記錄,并在成功回傳它后,更改記錄指向的某些成員。
所以想象一下我們在不同的執行緒中使用了上面的類,如下所示:
ThreadSafeMap g_map<std::string, Item>;
// thread 1
Item& item = g_map.at(key);
item.value ;
// thread 2
Item& item = g_map.at(key);
item.value--;
// thread 3
g_map.erase(key);
g_map[key] = newItem;
天真地,您認為這些操作是執行緒安全的,因為哈希表本身是執行緒安全的。但他們不是。執行緒 1 和執行緒 2 都試圖訪問鎖外的同一個專案。執行緒 3 甚至試圖替換其他兩個執行緒可能參考的記錄。這里有很多未定義的行為。
解決方案?堅持使用單執行緒哈希表實作并在應用程式/事務級別使用互斥鎖。更好的:
std::unordered_map<std::string, Item> g_map;
std::mutex g_mutex;
// thread 1
{
std::lock_guard lck(g_mutex);
Item& item = g_map.at(key);
item.value ;
}
// thread 2
{
std::lock_guard lck(g_mutex);
Item& item = g_map.at(key);
item.value--;
}
// thread 3
{
std::lock_guard lck(g_mutex);
g_map.erase(key);
g_map[key] = newItem;
}
底線。不要只是在低級資料結構上粘貼互斥鎖和鎖,并宣稱它是執行緒安全的。在呼叫者期望在哈希表本身上執行其操作集的級別使用互斥鎖和鎖。
uj5u.com熱心網友回復:
避免資料競爭的最可靠和適當的方法是使用互斥鎖序列化對哈希表的訪問;即每個執行緒在對哈希表執行任何操作(讀或寫)之前需要獲取互斥鎖,并在完成后釋放互斥鎖。
不過,您可能正在尋找的是實作無鎖哈希表,但確保正確的多執行緒行為沒有鎖是極其困難的,如果您處于實作此類事情所需的技術水平,您不需要在 Stackoverflow 上詢問它。所以我強烈建議你要么堅持使用序列化訪問方法(它適用于 99% 的軟體,并且可以在沒有深入了解 CPU、快取架構、RAM、作業系統、調度程式的情況下正確實作、優化器、C 語言規范等)或者如果您必須使用無鎖資料結構,您可以從信譽良好的來源找到一個預制的資料結構來使用,而不是嘗試推出自己的資料結構。事實上,即使你想推出自己的,你也應該從查看作業示例的源代碼開始,了解他們在做什么以及他們為什么這樣做。
uj5u.com熱心網友回復:
所以你需要基本的執行緒同步還是什么?您必須在讀寫函式中使用互斥鎖、lock_guard 或其他一些機制來進行執行緒同步。在 cppreference.com 中,您有標準庫的檔案。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/353728.html
