我有這個多線程的代碼,它試圖使用stl unordered_map
創建一個線程本地單例對象。多線程訪問unordered_map的運行時錯誤
這是code。我在這裏複製的代碼逐字:
#include <iostream>
#include <unordered_map>
#include <vector>
#include <thread>
#include <algorithm>
#include <mutex>
using namespace std;
class single
{
public:
// Every thread needs to call this to get its individual instance
static single* getInstance(unsigned int threadId);
static void print(unsigned int threadId)
{
std::cout << "threadId:" << threadId << ", Singleton: " << _instances[threadId] << std::endl;
}
protected:
// Made protected not private so that singleton can be subclassed
single(); // Clients cant construct objects directly
~single(); // cannot be destroyed by clients
single(const single &) = delete; // non-copyable
single& operator=(const single &) = delete; // can't be copy assigned
single(single &&) = delete; // non-move constructible
single & operator=(single &&) = delete; // non-move assignable
private:
static std::unordered_map<unsigned,single*> _instances;
static std::mutex _lock;
};
std::mutex single::_lock;
std::unordered_map<unsigned,single*> single::_instances;
single::single(){}
single::~single(){}
single* single::getInstance(unsigned int threadId)
{
if(_instances.count(threadId) == 0)
{
std::lock_guard<std::mutex> lg(_lock);
if(_instances.count(threadId) == 0)
{
_instances[threadId] = new single;
std::cout <<"Created By ThreadId: " << threadId <<std::endl;
}
}
return _instances[threadId];
}
void Run(unsigned int threadId)
{
single::getInstance(threadId)->print(threadId);
}
int main()
{
std::vector<std::thread> workers;
const unsigned threadCount = 16;
for(auto i = 0; i != threadCount; ++i)
{
workers.push_back(std::thread(Run, i));
}
for_each(workers.begin(), workers.end(), std::mem_fn(&thread::join));
return 0;
}
我用unordered_map
的count()
常數函數(線程安全的?),以檢查是否該線程的實例被創建。如果計數爲0,則創建實例並將其作爲密鑰的值存儲爲threadId
。我還添加了一個lock_guard
以防止多線程併發插入到無序的映射靜態對象,但它似乎是一些競爭條件,我無法發現並且此代碼有時會出錯。
任何人都可以向我解釋在這段代碼的哪部分競爭條件正在發生,如果這個解決方案可以使線程本地單身人士工作?
好吧,我提到count()是一個常量函數。它不是線程安全的,因爲它只讀取和不修改地圖狀態? – nurabha
'const'意味着'count()'函數不會改變容器的內容,並不意味着其他函數不會同時更改內容(數據爭用)。 – Niall
關於您的代碼遷移建議,我最初是這麼做的。但它不起作用。所以我想我應該嘗試使用雙重檢查鎖定來防止競爭條件_instances.count() – nurabha