I have a function where the statement foo
should be executed under lock_guard
but only when a pointer to a mutex
object has been provided to the function as a parameter. Otherwise foo
does not have to be protected by lock_guard
.
I cannot use the lock_guard
within an if
because the lock will be released immediately when the if
block ends.
so, this code is nonsense:
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 }
I tried something like this:
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... }
More or less, the only one possible solution for me is to repeat foo
:
bar( std::mutex * optionalMutex = nullptr ) { ... if ( nullptr != optionalMutex ) { std::lock_guard<std::mutex> lockScope( *optionalMutex ); foo... } else { foo... } }
The compiler gcc 4.9.3
does not compile the 2nd example and complains: error: expected primary-expression before 'lockScope'
. Update: Superlokkus explained in his answer why.
But I do want to avoid any code duplicates and therefore also the duplicate foo
.
My question:
Is there an elegant way how to implement this problem and not to use duplicate foo
. I know, I could use a lambda function to group foo
but I am curious if there is an another solution.
std::lock_guard A lock guard is an object that manages a mutex object by keeping it always locked. On construction, the mutex object is locked by the calling thread, and on destruction, the mutex is unlocked.
A lock_guard always holds a lock from its construction to its destruction. A unique_lock can be created without immediately locking, can unlock at any point in its existence, and can transfer ownership of the lock from one instance to another.
@Prab, just to give the explanation: unique_lock() is automatically released when the destructor is called meaning it is exception safe and that it automatically unlocks when you leave scope.
And that's why lock_guard isn't deprecated. scoped_lock and unique_lock may be a superset of functionality of lock_guard , but that fact is a double-edged sword. Sometimes it is just as important what a type won't do (default construct in this case).
class lock_guard; (since C++11) The class lock_guard is a mutex wrapper that provides a convenient RAII-style mechanism for owning a mutex for the duration of a scoped block. When a lock_guard object is created, it attempts to take ownership of the mutex it is given.
A mutex (regardless which implementation is used) must be acquired and released and that implies calls to the non-constant lock () and unlock () methods. So the argument to lock_guard cannot be logically const (as the mutex would be if the method was const ). The solution to this problem is to make the mutex mutable.
When notifying a condition_variable, default to doing it before you unlock the mutex instead of after. Rationale: It is never wrong to unlock after the notify, and it is sometimes wrong to unlock before the notify. And there is no performance penalty in unlocking after, as good implementations take this coding pattern into account.
One of the differences between std::lock_guard and std::unique_lock is that the programmer is able to unlock std::unique_lock, but she/he is not able to unlock std::lock_guard. Let’s explain it in more detail. then the constructor of guard1 locks the mutex.
How about this one?
void bar(std::mutex * optionalMutex = nullptr) { auto lockScope = (optionalMutex == nullptr) ? std::unique_lock<std::mutex>() : std::unique_lock<std::mutex>(*optionalMutex); }
Explanation: Your compiler had trouble with your prior statement because, you can not suddenly change the type of the ternary ?
expression; i.e. the literal 0
is not a std::lock_guard
and vice versa. So I changed the two branches to the same type, here std::unique_lock<std::mutex>
because lock_guard
isn't designed be used without a valid mutex. But still prefer std::lock_guard
over std::unique_lock
in the simpler cases, because it will make your code more readable.
Also your statement wasn't viable for the compiler, i.e. even syntactical correct, because the variable lockScope would only have existed in one branch.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With