Reputation: 22134
I have two use cases.
A. I want to synchronise access to a queue for two threads.
B. I want to synchronise access to a queue for two threads and use a condition variable because one of the threads will wait on content to be stored into the queue by the other thread.
For use case A I see code example using std::lock_guard<>
. For use case B I see code example using std::unique_lock<>
.
What is the difference between the two and which one should I use in which use case?
Upvotes: 541
Views: 286269
Reputation: 54589
Use lock_guard
unless you need to be able to manually unlock
the mutex in between without destroying the lock
.
In particular, condition_variable
unlocks its mutex when going to sleep upon calls to wait
. That is why a lock_guard
is not sufficient here.
If you're already on C++17 or later, consider using scoped_lock
as a slightly improved version of lock_guard
, with the same essential capabilities.
Upvotes: 80
Reputation: 34518
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
.
Upvotes: 521
Reputation: 69
One missing difference is:
std::unique_lock
can be moved but std::lock_guard
can't be moved.
Note: Both cant be copied.
Upvotes: 5
Reputation: 181
They are not really same mutexes, lock_guard<muType>
has nearly the same as std::mutex
, with a difference that it's lifetime ends at the end of the scope (D-tor called) so a clear definition about these two mutexes :
lock_guard<muType>
has a mechanism for owning a mutex for the duration of a scoped block.
And
unique_lock<muType>
is a wrapper allowing deferred locking, time-constrained attempts at locking, recursive locking, transfer of lock ownership, and use with condition variables.
Here is an example implemetation :
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <chrono>
using namespace std::chrono;
class Product{
public:
Product(int data):mdata(data){
}
virtual~Product(){
}
bool isReady(){
return flag;
}
void showData(){
std::cout<<mdata<<std::endl;
}
void read(){
std::this_thread::sleep_for(milliseconds(2000));
std::lock_guard<std::mutex> guard(mmutex);
flag = true;
std::cout<<"Data is ready"<<std::endl;
cvar.notify_one();
}
void task(){
std::unique_lock<std::mutex> lock(mmutex);
cvar.wait(lock, [&, this]() mutable throw() -> bool{ return this->isReady(); });
mdata+=1;
}
protected:
std::condition_variable cvar;
std::mutex mmutex;
int mdata;
bool flag = false;
};
int main(){
int a = 0;
Product product(a);
std::thread reading(product.read, &product);
std::thread setting(product.task, &product);
reading.join();
setting.join();
product.showData();
return 0;
}
In this example, i used the unique_lock<muType>
with condition variable
Upvotes: -2
Reputation: 1247
There are certain common things between lock_guard
and unique_lock
and certain differences.
But in the context of the question asked, the compiler does not allow using a lock_guard
in combination with a condition variable, because when a thread calls wait on a condition variable, the mutex gets unlocked automatically and when other thread/threads notify and the current thread is invoked (comes out of wait), the lock is re-acquired.
This phenomenon is against the principle of lock_guard
. lock_guard
can be constructed only once and destructed only once.
Hence lock_guard
cannot be used in combination with a condition variable, but a unique_lock
can be (because unique_lock
can be locked and unlocked several times).
Upvotes: 12
Reputation: 727
As has been mentioned by others, std::unique_lock tracks the locked status of the mutex, so you can defer locking until after construction of the lock, and unlock before destruction of the lock. std::lock_guard does not permit this.
There seems no reason why the std::condition_variable wait functions should not take a lock_guard as well as a unique_lock, because whenever a wait ends (for whatever reason) the mutex is automatically reacquired so that would not cause any semantic violation. However according to the standard, to use a std::lock_guard with a condition variable you have to use a std::condition_variable_any instead of std::condition_variable.
Edit: deleted "Using the pthreads interface std::condition_variable and std::condition_variable_any should be identical". On looking at gcc's implementation:
Upvotes: -5
Reputation: 71899
lock_guard
and unique_lock
are pretty much the same thing; lock_guard
is a restricted version with a limited interface.
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.
So you always use lock_guard
, unless you need the capabilities of unique_lock
. A condition_variable
needs a unique_lock
.
Upvotes: 174