boost::unique_lock vs boost::lock_guard

C++BoostLocking

C++ Problem Overview


I don't well understand the difference betweeen these two lock classes. In boost documentation it is said, boost::unique_lock doesn't realize lock automatically.

Does it mean that the main difference between unique_lock and lock_guard is that with unique_lock we must call explicitly the lock() function ?

C++ Solutions


Solution 1 - C++

First to answer your question. No you don't need to call lock on a unique_lock. See below:

The unique_lock is only a lock class with more features. In most cases the lock_guard will do what you want and will be sufficient.
The unique_lock has more features to offer to you. E.g a timed wait if you need a timeout or if you want to defer your lock to a later point than the construction of the object. So it highly depends on what you want to do. BTW: The following code snippets do the same thing.

boost::mutex mutex;
boost::lock_guard<boost::mutex> lock(mutex);

boost::mutex mutex;
boost::unique_lock<boost::mutex> lock(mutex);

The first one can be used to synchronize access to data, but if you want to use condition variables you need to go for the second one.

Solution 2 - C++

The currently best voted answer is good, but it did not clarify my doubt till I dug a bit deeper so decided to share with people who might be in the same boat.

Firstly both lock_guard and unique_lock follows the RAII pattern, in the simplest use case the lock is acquired during construction and unlocked during destruction automatically. If that is your use case then you don't need the extra flexibility of unique_lock and lock_guard will be more efficient.

The key difference between both is a unique_lock instance doesn't need to always own the mutex it is associated with while in lock_guard it owns the mutex. This means unique_lock would need to have an extra flag indicating whether it owns the lock and another extra method 'owns_lock()' to check that. Knowing this we can explain all extra benefits this flags brings with the overhead of that extra data to be set and checked

  1. Lock doesn't have to taken right at the construction, you can pass the flag std::defer_lock during its construction to keep the mutex unlocked during construction.
  2. We can unlock it before the function ends and don't have to necessarily wait for destructor to release it, which can be handy.
  3. You can pass the ownership of the lock from a function, it is movable and not copyable.
  4. It can be used with conditional variables since that requires mutex to be locked, condition checked and unlocked while waiting for a condition.

Solution 3 - C++

Their implementation can be found under path .../boost/thread/locks.hpp - and they are sitting just one next to other :) To sum things short:

lock_guard is a short simple utility class that locks mutex in constructor and unlocks in destructor, not caring about details.

unique_lock is a bit more complex one, adding pretty lot of features - but it still locks automatically in constructor. It is called unique_lock because it introduces "lock ownership" concept ( see owns_lock() method ).

Solution 4 - C++

If you're used to pthreads(3):

  • boost::mutex = pthread_mutex_*
  • boost::unique_lock = pthread_rwlock_* used to obtain write/exclusive locks (i.e. pthread_rwlock_wrlock)
  • boost::shared_lock = pthread_rwlock_* used to obtain read/shared locks (i.e. pthread_rwlock_rdlock)

Yes a boost::unique_lock and a boost::mutex function in similar ways, but a boost::mutex is generally a lighter weight mutex to acquire and release. That said, a shared_lock with the lock already acquired is faster (and allows for concurrency), but it's comparatively expensive to obtain a unique_lock.

You have to look under the covers to see the implementation details, but that's the gist of the intended differences.


Speaking of performance: here's a moderately useful comparison of latencies:

http://www.eecs.berkeley.edu/%7Ercs/research/interactive_latency.html

It would be nice if I/someone could benchmark the relative cost of the different pthread_* primitives, but last I looked, pthread_mutex_* was ~25us, whereas pthread_rwlock_* was ~20-100us depending on whether or not the read lock had been already acquired (~10us) or not (~20us) or writer (~100us). You'll have to benchmark to confirm current numbers and I'm sure it's very OS specific.

Solution 5 - C++

I think unique_lock may be also used when you need to emphasize the difference between unique and shared locks.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionGuillaume ParisView Question on Stackoverflow
Solution 1 - C++mkaesView Answer on Stackoverflow
Solution 2 - C++jayadevView Answer on Stackoverflow
Solution 3 - C++Mihails StrasunsView Answer on Stackoverflow
Solution 4 - C++SeanView Answer on Stackoverflow
Solution 5 - C++Aleksei PetrenkoView Answer on Stackoverflow