Условное использование std:: lock_guard
У меня есть функция, в которой оператор foo
должен выполняться в lock_guard
, но только тогда, когда указатель на объект mutex
был предоставлен функции в качестве параметра. В противном случае foo
не должен быть защищен lock_guard
.
Я не могу использовать lock_guard
внутри if
, потому что блокировка будет немедленно выпущена, когда заканчивается блок if
.
так что этот код вздор:
bar( std::mutex * optionalMutex = nullptr )
{
...
if ( nullptr != optionalMutex ) {
std::lock_guard<std::mutex> lockScope( *optionalMutex );
} <- Here the lock ends
foo... <- foo is not protected when optionalMutex was provided
}
Я пробовал что-то вроде этого:
bar( std::mutex * optionalMutex = nullptr )
{
...
nullptr == optionalMutex ? 0 : std::lock_guard<std::mutex> lockScope( *optionalMutex );
// this scope should be protected by lock_guard when optionalMutex was provided
foo...
}
Более или менее единственное возможное решение для меня - повторить foo
:
bar( std::mutex * optionalMutex = nullptr )
{
...
if ( nullptr != optionalMutex ) {
std::lock_guard<std::mutex> lockScope( *optionalMutex );
foo...
} else {
foo...
}
}
Компилятор gcc 4.9.3
не компилирует второй пример и жалуется: error: expected primary-expression before 'lockScope'
. Обновление: Superlokkus объяснил в своем ответе почему.
Но я хочу избежать дубликатов кода и, следовательно, дублировать foo
.
Мой вопрос:
Есть ли элегантный способ реализации этой проблемы и не использовать дубликат foo
. Я знаю, я мог бы использовать лямбда-функцию для группировки foo
, но мне любопытно, есть ли другое решение.
Ответы
Ответ 1
Как насчет этого?
void bar(std::mutex * optionalMutex = nullptr)
{
auto lockScope = (optionalMutex == nullptr) ?
std::unique_lock<std::mutex>()
: std::unique_lock<std::mutex>(*optionalMutex);
}
Объяснение: У вашего компилятора были проблемы с вашим предыдущим утверждением, потому что вы не можете внезапно изменить тип тернарного выражения ?
; то есть буква 0
не является std::lock_guard
и наоборот. Поэтому я изменил две ветки на один и тот же тип, здесь std::unique_lock<std::mutex>
, потому что lock_guard
не предназначен для использования без действительного мьютекса. Но по-прежнему предпочитайте std::lock_guard
более std::unique_lock
в более простых случаях, потому что это сделает ваш код более читаемым.
Также ваше утверждение не было жизнеспособным для компилятора, то есть даже синтаксически корректным, поскольку переменная lockScope существовала бы только в одной ветки.
Ответ 2
У вас действительно есть две функции, одна из которых блокируется, а другая - нет. Первый может вызвать второе:
void bar() {
// whatever
}
void bar(std::mutex* mtx) {
std::lock_guard<std::mutex> lockScope(*mtx);
bar();
}
Ответ 3
У меня есть только это решение. Использование фиктивного объекта mutex
:
Код:
bar( std::mutex * optionalMutex = nullptr )
{
...
std::mutex dummyMutex;
std::lock_guard<std::mutex> lockScope( optionalMutex ? *optionalMutex, dummyMutex );
foo... <- NOW foo is protected when optionalMutex was provided
}
Ответ 4
Это второстепенная проблема, но вы можете избежать передачи необработанного указателя, разрешив вызывающему абоненту вместо std:: unique_lock:
bar( std::unique_lock<std::mutex> lockScope )
{
if(lockScope.mutex())
{
lockScope.lock();
//do things
}
}
Это похоже на более четкое выражение интерфейса и снижает вероятность злоупотребления.
Ответ 5
Ответ от Superlockus достаточно хорош, но мне интересно, почему вы просто не пишете его так:
bar( std::mutex * optionalMutex = nullptr )
{
if (optionalMutex)
optionalMutex->lock():
foo...
if (optionalMutex)
optionalMutex->unlock():
}
lock_guard
и unique_lock
являются удобными, но не единственным способом.