Безымянный lock_guard
Бывает иногда, что при определение объекта забываешь что-то написать. Параметры конструктора, шаблонный параметр класса, точка с запятой - всякое бывает. Такое обычно спокойно детектируется на этапе компиляции и без проблем исправляется. Но вот есть одна "забывашка", которая может привести к действительно неприятным последствиям и не так просто детектируется.
Пишите вы такие критическую секцию. Например, просто хотите потокобезопасно обновить мапу. Как полагается в книжках, используете std::lock_guard, но забываете одну деталь.
// std::map<std::string, std::string> SomeClass::map_;
// std::mutex SomeClass::mtx_;
bool SomeClass::UpdateMap(const std::string& key, const std::string& value) {
std::lock_guard{mtx_};
auto result = map_.insert({key, value});
return result.second;
}
Для С++17 вполне синтаксически верный код. И он будет запускаться.
Но потокобезопасности не будет.
"Но как же! Я же использовал lock_guard!"
Да вот только этот
lock_guard безымянный. То есть является временным объектом. Соответственно, его деструктор вызовется ровно до insert'а и мьютекс освободится. А значит ничего безопасного тут нет.
Мапа будет теперь постоянно в неконсистентном состоянии и удачи потом в поиске этого места, особенно с замыленным глазом.
Вроде банальная ошибка. На работе ее еще можно через ревью отловить. Но на пет-проектах или собесах даже опытные люди ее совершают. Так что будьте бдительны в следующий раз и будет вам многопоточное счастье!
Stay alerted. Stay cool.
#cppcore