以相对最简单的形式考虑线程安全的getter方法:
std::map<std::string, boost::shared_ptr<AAA> > repo;
AAA & get(const std::string &key)
{
boost::upgrade_lock<boost::shared_mutex> lock(repoMutex);
std::map<std::string, boost::shared_ptr<AAA> >::iterator it = repo.find(key);
if(it == repo.end()){
boost::upgrade_to_unique_lock<boost::shared_mutex> uniqueLock(lock);
boost::shared_ptr<AAA> t(new AAA(key));
repo.insert(std::make_pair(key,t));
return *t;
}
return *it->second;
}
在上面,我使用共享(可升级)锁来保护查找操作,并且仅当我需要插入键/值时才升级到唯一锁。到目前为止还不错?
我想象的是以下内容(如果在任何步骤中我的概念是错误的,请告诉我):
两个主题进入方法
允许两者同时为同一个键运行repo.find()
(并且密钥不存在)。
两个都失败了。因为密钥不存在。
第一个线程通过进入升级区域获得独占访问权限,而第二个线程则等待进入升级区域。
第一个线程完成了为key创建新条目的工作 走开
第二个帖子进入, 覆盖 第一个帖子插入的键/值。(这不是任何人想要的)
我们如何解决这个问题?感谢
答案 0 :(得分:1)
简而言之,您当前的解决方案几乎没有任何问题。
首先,第二个线程不会覆盖第一个线程写入的数据,因为map::insert()只插入新密钥。您只需检查insert
是否确实插入了元素并返回相应的值。
唯一担心的是可能无意中创建t
。在这种情况下,您可以在锁定后添加另一个检查:
std::map<std::string, boost::shared_ptr<AAA> >::iterator it = repo.find(key);
if(it == repo.end()){
boost::upgrade_to_unique_lock<boost::shared_mutex> uniqueLock(lock);
if (repo.find(key) == repo.end() {
...
}
}
但是你应该对你的代码进行分析,看看这是否会给你带来任何好处。
此外,您可以使用提示map::insert()
来避免双重搜索密钥:
std::map<std::string, boost::shared_ptr<AAA> >::iterator it = repo.find(key);
if(it == repo.end()){
boost::upgrade_to_unique_lock<boost::shared_mutex> uniqueLock(lock);
it = repo.lower_bound(key);
if (it->first != key)
boost::shared_ptr<AAA> t(new AAA(key));
repo.insert(it, std::make_pair(key,t));
return *t;
} else {
return *it->second;
}
}