путаница с использованием обновляемой блокировки на std :: map’s find / insert

Рассмотрим поточно-безопасный метод получения в его относительно простой форме:

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. Первый поток завершает свою работу по созданию новой записи для ключа, а затем
    уходит

  6. вторая нить входит и перезаписывает ключ / значение, вставленное первым потоком. (это не то, что кто-то хочет)

Как мы решаем эту проблему? Спасибо

2

Решение

Короче говоря, в вашем текущем решении нет ничего плохого.

Прежде всего, второй поток не будет перезаписывать данные, записанные первым, потому что карта :: вставить () вставляет только новые ключи. Вам нужно только проверить, если 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;
}
}
1

Другие решения


По вопросам рекламы ammmcru@yandex.ru
Adblock
detector