我有这个多线程代码,它试图使用stl 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
的{{1}}常量函数(线程安全?)来检查线程的实例是否已创建。如果计数为0,我创建实例并将其作为密钥的值存储为count()
。我还添加了一个threadId
来防止多个线程并发插入到无序映射静态对象,但似乎它们是一些我无法发现的竞争条件,这些代码有时会出错。
任何人都可以向我解释这段代码的哪一部分正在发生竞争条件,是否可以使这个解决方案适用于线程本地单例?
答案 0 :(得分:3)
任何人都可以向我解释这段代码的哪一部分正在发生竞争......
不,它不是线程安全的。一旦有至少一个写入者,所有访问都需要被锁保护。因此,第一个_instances.count()
无人看守,可能导致数据竞争。
...如果这个解决方案可以用于线程本地单例?
您可以使用thread_local
来控制本地线程存储持续时间。
您还可以将锁定保护程序迁移到unordered_map
上的任何访问权限之前。
single* single::getInstance( unsigned int threadId )
{
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];
}
关于线程计数的附注,根据要解决的问题,您可以将线程数限制为std::thread::hardware_concurrency
限制。