混淆使用std :: map的find / insert上的可升级锁

时间:2014-09-26 10:50:34

标签: c++ multithreading mutex boost-mutex

以相对最简单的形式考虑线程安全的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;
}

在上面,我使用共享(可升级)锁来保护查找操作,并且仅当我需要插入键/值时才升级到唯一锁。到目前为止还不错?

我想象的是以下内容(如果在任何步骤中我的概念是错误的,请告诉我):

  1. 两个主题进入方法

  2. 允许两者同时为同一个键运行repo.find()(并且密钥不存在)。

  3. 两个都失败了。因为密钥不存在。

  4. 第一个线程通过进入升级区域获得独占访问权限,而第二个线程则等待进入升级区域。

  5. 第一个线程完成了为key创建新条目的工作 走开

  6. 第二个帖子进入, 覆盖 第一个帖子插入的键/值。(这不是任何人想要的)

  7. 我们如何解决这个问题?感谢

1 个答案:

答案 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;
    }
}