std::unique_lock std::mutex or std::lock_guard std::mutex ?
The difference is that you can lock and unlock a std::unique_lock
. std::lock_guard
will be locked only once on construction and unlocked on destruction.
So for use case B you definitely need a std::unique_lock
for the condition variable. In case A it depends whether you need to relock the guard.
std::unique_lock
has other features that allow it to e.g.: be constructed without locking the mutex immediately but to build the RAII wrapper (see here).
std::lock_guard
also provides a convenient RAII wrapper, but cannot lock multiple mutexes safely. It can be used when you need a wrapper for a limited scope, e.g.: a member function:
class MyClass{
std::mutex my_mutex;
void member_foo() {
std::lock_guard<mutex_type> lock(this->my_mutex);
/*
block of code which needs mutual exclusion (e.g. open the same
file in multiple threads).
*/
//mutex is automatically released when lock goes out of scope
}
};
To clarify a question by chmike, by default std::lock_guard
and std::unique_lock
are the same.
So in the above case, you could replace std::lock_guard
with std::unique_lock
. However, std::unique_lock
might have a tad more overhead.
Note that these days (since, C++17) one should use std::scoped_lock
instead of std::lock_guard
.
std::scoped_lock or std::unique_lock or std::lock_guard?
The two objects are for different purposes. scoped_lock
is for the simple case of wanting to lock some number of mutex objects in a deadlock-free way. Locking a single mutex is just a special case of locking multiple ones. The object is completely immobile, and it's very simple.
unique_lock
provides a number of features, few of which are especially applicable when simultaneously locking multiple mutexes.
Deferred locking. Deferring would have to be all or nothing; you either defer locking all the mutexes or none of them. It's not clear why you would want to defer locking a series of mutexes, since you would have to relinquish any locks that succeeded if any of them failed.
Timeout locks. If you want a timeout of 100ms, does that mean that locking all of the mutexes should take no more than 100ms? That is, if the first 3 lock immediately, but the next one takes 75ms, should it be considered a timeout if the fifth takes 30ms?
Adoption of mutexes. The whole point of locking multiple mutexes in a single operation is to be able to avoid deadlocks. This is done by locking the mutexes in an order that is globally consistent. That is, any place where you lock those mutex objects with
std::lock
equivalent calls will lock them in the same order, no matter what.If one of the mutexes has already been locked (and thus the lock should be adopted), then it was locked outside of
std::lock
, and thus you have no guarantee that it was locked in the globally consistent order. And that ignores the difficulty of specifying which mutexes to adopt and which ones to lock.Transfer of ownership (being moveable). This is a dubious prospect for multiple mutexes for similar reasons as adopting locks. The guarantees against deadlocks only work if a single call to
std::lock
or equivalent locks all of the mutexes of interest. If you're moving ownership of thesescoped_lock
s around, it becomes very easy to be at a point in code where you have multiplescoped_lock
s in the same scope, when you could have locked all of them in one go. This courts the very kind of deadlock thatstd::lock
was created to avoid.
Note that std::lock
(the basis of scoped_lock
's functionality) doesn't even try to provide any of these features.
Could there be a specialization of scoped_lock
which took only one mutex type that offered the behavior of unique_lock
? Sure. But that would violate the purpose of scoped_lock
, which was to be a deadlock-safe locker for multiple mutexes. It only obsoleted lock_guard
by accident, since it had the identical interface in the case of a single mutex.
Besides, having template specializations with vastly different interfaces and capabilities doesn't usually work out well. See vector<bool>
as an example.
Does std::lock_guard release the mutex after constructed with std::adopt_lock option?
When you constructed a std::unique_lock
to manage the mutex, you should stick to it unless you first break the association of the std::unique_lock
with the mutex using std::unique_lock::release. In your sample, you touched the raw mutex when it's still managed by a std::unique_lock
and this is wrong.
is it safe to use the same mutex with lock_gard and without it in other parts of code
Yes, you can effectively mix and match different guard instances (e.g. lock_guard, unique_lock, etc...) with std::mutex
in different functions. One case I run into occassionally is when I want to use std::lock_guard
for most methods, but usage of std::condition_variable
expects a std::unique_lock
for its wait
method.
To elaborate on what Oblivion said, I typically introduce a new scope block within a function so that usage of std::lock_guard
is consistent. Example:
void func2() {
{ // ENTER LOCK
lock_guard<std::mutex> lck;
//some code that should not be executed when func1 is executed
} // EXIT LOCK
// some other (thread safe) code
}
The advantage of the using the above pattern is that if anything throws an exception within the critical section of code that is under a lock, the destructor of lck
will still be invoked and hence, unlock the mutex.
Difference between std::mutex lock function and std::lock_guard std::mutex ?
Using lock_guard
automatically unlocks the mutex again when it goes out of scope. That makes it impossible to forget to unlock it, when returning, or when an exception is thrown. You should always prefer to use lock_guard
or unique_lock
instead of using mutex::lock()
. See http://kayari.org/cxx/antipatterns.html#locking-mutex
lock_guard
is an example of an RAII or SBRM type.
How can std::lock_guard be faster than std::mutex::lock()?
The release build produces the same result for both versions.
The DEBUG
build shows ~33% longer time for func2
; the difference I see in the disassembly that func2
uses __security_cookie
and invokes @_RTC_CheckStackVars@8
.
Are you timing DEBUG?
EDIT:
Additionally, while looking at RELEASE
disassembly, I noticed that mutex
methods were saved in two registries:
010F104E mov edi,dword ptr [__imp___Mtx_lock (010F3060h)]
010F1054 xor esi,esi
010F1056 mov ebx,dword ptr [__imp___Mtx_unlock (010F3054h)]
and called the same way from both func1
and func2
:
010F1067 call edi
....
010F107F call ebx
Related Topics
Forward Declare a Standard Container
Getdibits and Loop Through Pixels Using X, Y
How to Set Up Googletest as a Shared Library on Linux
Does the C++ Volatile Keyword Introduce a Memory Fence
Visual Studio 2015 Doesn't Have Cl.Exe
How to Strip All Non Alphanumeric Characters from a String in C++
Building a 32-Bit Float Out of Its 4 Composite Bytes
What Is a Glibc Free/Malloc/Realloc Invalid Next Size/Invalid Pointer Error and How to Fix It
C++: Rationale Behind Hiding Rule
C++ Memory Model and Race Conditions on Char Arrays
Blending Does Not Remove Seams in Opencv
Connected Components in Opencv