unique_lock or lock_guard: Which Is Better?
Last Updated :
08 May, 2025
In C++, to manage access to shared resources, the STL (standard template library) of C++, provides synchronization mechanisms such as lock_guard and unique_lock. Both are useful for managing mutex but have different features and use cases.
In this article, we will discuss the advantages, disadvantages, differences, and use cases of both unique_lock and lock_guard in C++
unique_lock
unique_lock offers more flexibility than lock_guard. It provides features like manual locking and unlocking, deferred locking, and ownership transfer. Unlike lock_guard, which automatically locks and unlocks the mutex, unique_lock requires explicit calls to lock and unlock. The following is the syntax to use unique_lock in C++:
Syntax
C++
shared_lock<mutex> Name(myMutex, lockingBehavior);
where,
- name: Name assign to shared_lock object
- myMutex: It is a placeholder for the actual type of the mutex.
- lockingBehavior: This is the optional parameter, which determines how the mutex is locked and managed.
Example
The following program illustrates the use of unique_lock in C++:
C++
// C++ Program using std::unique_lock
#include <mutex>
#include <thread>
#include <iostream>
using namespace std;
// Global mutex to protect shared_data
mutex mtx;
// Shared data variable
int shared_data = 0;
// Function to increment shared_data
void increment_data() {
// Create a unique_lock object, but defer locking the mutex
unique_lock<mutex> lock(mtx, defer_lock);
// Explicitly acquire the lock
lock.lock();
// Critical section: safely modify shared_data
shared_data += 2;
// Manually release the lock
lock.unlock();
}
int main() {
// Create two threads that run the increment_data function
thread t1(increment_data);
thread t2(increment_data);
// Wait for both threads to finish
t1.join();
t2.join();
// Output the value of shared_data
cout << "Value of shared variable: " << shared_data;
return 0;
}
Output
Value of shared variable: 4
Time Complexity: O(1)
Auxiliary Space: O(1)
Key Features
Following are some key features of unique_lock:
- Flexibility: Can lock and unlock multiple times within its scope.
- Deferred Locking: This can be constructed without locking the mutex immediately.
- Timed Locking: Supports times and try-locking operations.
- Ownership Transfer: It allows transferring mutex ownership to another unique_lock.
Usage
Following are the use cases when you should consider using unique_lock:
- You need more control over the locking mechanism including ability to lock and unlock manually.
- You need to defer locking or conditionally lock a mutex.
- You require timed locking to prevent blocking indefinitely.
- You need to transfer lock ownership between different scopes or threads.
lock_guard
In C++, lock_guard is a simple class that is used to manage the locking and unlocking of a mutex. Its main purpose is to automatically lock a mutex when it is created and automatically unlock it when the lock_guard object goes out of scope. Following is the syntax to use lock_guard in C++:
Syntax
C++
lock_guard<mutex> name(myMutex);
where,
- name: Name assign to shared_lock object
- myMutex: It is a placeholder for the actual type of the mutex.
Example
The following program illustrates the use of lock_guard in C++:
C++
// C++ Program using std::lock_guard
#include <mutex>
#include <thread>
#include<iostream>
using namespace std;
// Global mutex to protect shared_data
mutex mtx;
// Shared data variable
int shared_data = 0;
// Function to increment shared_data
void increment_data() {
// Create a lock_guard object which locks the mutex
lock_guard<mutex> lock(mtx);
// Critical section: safely modify shared_data
shared_data+=2;
// Lock is automatically released when 'lock' goes out of scope
}
int main() {
// Create two threads that run the increment_data function
thread t1(increment_data);
thread t2(increment_data);
// Wait for both threads to finish
t1.join();
t2.join();
// Output the value of shared_data
cout << "Value of shared variable: " << shared_data;
return 0;
}
Output
Value of shared variable: 4
Time Complexity: O(1)
Auxiliary Space: O(1)
Key Features
Following are some key features of unique_lock:
- Simplicity: lock_guard is very simple to use with minimal overhead.
- RAII(Resource Acquisition Is Initialization) : Ensures that mutex is released when the lock_guard goes out of scope.
- No Unlocking: Does not support manual unlocking before the end of its scope.
Usage
Following are the use cases when you should consider using lock_guard:
- It is used when you need simple lock that automatically unlocks when the scope ends.
- The locking operation is straightforward and does not require unlocking before scope ends.
- You prioritize minimal overhead and simplicity.
Difference between lock_guard and unique_lock
Following are some key differences between lock_guard and unique_lock in C++:
Features | lock_guard | unique_lock |
---|
Complexity | Simple and minimal overhead. | It is more complex and have additional overhead due to extra features. |
---|
Locking behavior | Locks mutex upon construction and destruction. | Can lock and unlock multiple times within its scope and supports deferred locking. |
---|
Flexibility | No manual control over locking and unlocking. | There is manual control over locking and unlocking. |
---|
Deferred Locking | Not supported. | Supported. |
---|
Timed Locking | Not supported. | Supported. |
---|
Ownership Transfer | Not supported. | Supported. |
---|
Ideal Usage | Used where simple and single-scope locking is needed. | Used in complex scenarios needing lock control and flexibility. |
---|
Exception Safety | Guarantees unlock on destruction | It guarantees unlock on destruction more control may increase error risk if misused |
---|
Overhead | Minimal | Slightly higher due to additional features. |
---|
Example Use Case | Simple critical sections. | Conditional locking, timed locking and transferring lock ownership. |
---|
Conclusion
In conclusion, both unique_lock and lock_guard are powerful tools provided by the C++ standard library for managing mutexes and ensuring thread safety. lock_guard offers a simple, efficient way to lock and unlock a mutex within a scope, making it ideal for straightforward locking needs.Choosing between them depends on the specific requirements of your code.
Similar Reads
std::unique_lock or std::lock_guard: Which Is Better? In C++, to manage access to shared resources, the STL (standard template library) of C++, provides synchronization mechanisms such as lock_guard and unique_lock. Both are useful for managing mutex but have different features and use cases.In this article, we will discuss the advantages, disadvantage
5 min read
How to wake up a std::thread while it is sleeping? In this article, we will discuss how to wake up a std::thread while it is sleeping. It is known that a thread can't be exited when it is sleeping. So it is woken up using a command as: std::condition_variable Below is the pseudo-code to implement the same:Â C++ // Custom Class struct MyClass { // Co
3 min read
How to Use the std::mutex Synchronization Primitive in C++ In multi-threaded programming, it is essential to ensure that shared resources are accessed in a controlled and synchronized manner to maintain data consistency and prevent race conditions. The std::mutex synchronization primitive was introduced in C++ 11 to allow threads to acquire exclusive owners
3 min read
Which Queue ensures uniqueness of the elements? A queue is a linear data structure that follows the principle of First In First Out (FIFO). The first element added to the queue will be the first element to be removed from the queue. The queue is a list in which all additions to the list are made at one end, and all deletions from the list are mad
7 min read
Why Can I Not push_back a unique_ptr into a Vector? In C++, the STL provides us with the containers and smart pointers that help us manage dynamic memory efficiently. However, we might encounter difficulties when trying to use std::unique_ptr with std::vector. In this article, we will learn why we cannot directly push_back a std::unique_ptr into a st
4 min read
What is the Python Global Interpreter Lock (GIL) Python Global Interpreter Lock (GIL) is a type of process lock which is used by python whenever it deals with processes. Generally, Python only uses only one thread to execute the set of written statements. This means that in python only one thread will be executed at a time. The performance of the
5 min read