std::unique_lock or std::lock_guard: Which Is Better?
Last Updated :
28 May, 2024
In C++, to manage access to shared resources, the STL (standard template library) of C++, provides synchronization mechanisms such as std::lock_guard and std::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 std::unique_lock and std::lock_guard in C++
std::unique_lock
The std::unique_lock class provides more flexibility as compared to std::lock_guard. It provides features like manual locking, unlocking, deferred locking, transfer of ownership, and many more to the users. std::unique_lock requires explicit locking and unlocking, it doesn't automatically acquire and release lock like std::lock_guard. Following is the syntax to use std::unique_lock in C++:
Syntax
std::unique_lock<std::mutex> lock(mutex);
// Mutex is now locked
// Write the Code for Critical section
// Optional: manually unlock before the lock object goes out of scope
lock.unlock();
// Optional: re-lock if needed
lock.lock();
where lock() and unlock() are methods to acquire and release the mutex.
C++ Program using std::unique_lock
The following program illustrates the use of std::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 std::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 std::unique_lock.
Usage
Following are the use cases when you should consider using std::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.
std::lock_guard
std::lock_guard is a simple RAII-style (Resource Acquisition Is Initialization) mutex wrapper that locks a mutex on creation and automatically unlocks it upon destruction. It provides a straightforward way to ensure that a mutex is properly released even if an exception is thrown. Following is the syntax to use std::lock_guard in C++:
Syntax
std::lock_guard<std::mutex> guard(mutex);
// Mutex is now locked
// Critical section
// Mutex is automatically unlocked when 'guard' goes out of scope
C++ Program using std::lock_guard
The following program illustrates the use of std::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 std::unique_lock:
- Simplicity: std::lock_guard is very simple to use with minimal overhead.
- RAII(Resource Acquisition Is Initialization) : Ensures that mutex is released when the std::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 std::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 std::lock_guard and std::unique_lock
Following are some key differences between std::lock_guard and std::unique_lock in C++:
Features
| std::lock_guard
| std::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 std::unique_lock and std::lock_guard are powerful tools provided by the C++ standard library for managing mutexes and ensuring thread safety. std::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
How to Create Vectors of Unique Pointers in C++?
In C++, the vector is defined as a dynamic array that can grow or shrink in size. Vectors of unique pointers are commonly used to manage the collections of dynamically allocated objects as they combine the dynamic resizing capability of vectors with the automatic memory management provided by unique
2 min read
Why it is important to write "using namespace std" in C++ program?
In this article, we will discuss the use of "using namespace std" in the C++ program. Need of Namespace in C++As the same name can't be given to multiple variables, functions, classes, etc. in the same scope. So, to overcome this situation, namespace is introduced. Example Below is the C++ program i
4 min read
Difference between std::set vs std::vector in C++ STL
Vectors: Vectors are containers similar to dynamic arrays, with the ability to resize when a new element is inserted or deleted from it. It is a template of Standard Template Library or STL, which provides more flexibility to the program. Elements of vectors are placed in contiguous storage and are
3 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/C++ Code // Custom Class struct MyClass {
3 min read
Remove duplicates from a sorted array using STL in C++
Given a sorted array, the task is to remove the duplicate elements from the array using STL in C++ Examples: Input: arr[] = {2, 2, 2, 2, 2} Output: arr[] = {2} Input: arr[] = {1, 2, 2, 3, 4, 4, 4, 5, 5} Output: arr[] = {1, 2, 3, 4, 5} Approach: The duplicates of the array can be removed using the un
2 min read
How to Create Deque of Unordered_Set in C++?
In C++, a deque (double-ended queue) is a container that allows insertion and removal of elements from both ends whereas an unordered set is a container that stores unique elements in no particular order. In this article, we will learn how to create a deque of unordered sets in C++ STL. Example:Inpu
2 min read
How to Find the Unique Elements in an Array in C++?
In C++, an array is a data structure that is used to store multiple values of similar data types in a contiguous memory location. In this article, we will learn how to find the unique elements in an array in C++. Example: Input: array = {1, 2, 1, 2, 2, 3, 4} Output: Unique elements in the array: 1 2
2 min read
When to Use List Instead of Vector in C++?
In C++, both std::vector and std::list are sequence containers that can store a collection of elements. However, they have different characteristics and use cases. In this article, we will learn when to use a list instead of a vector in C++. When to Prefer List Instead of Vector?Vectors are sequence
4 min read
class std::string_view in C++17
The std::string has some demerits, one of the most common situations is constant strings. Below is the program that demonstrates the problem that occurs in dealing with constant strings with std::string: Program 1: C/C++ Code // C++ program to demonstrate the // problem occurred in string #include
12 min read
What is the difference between Set vs Hashset in C++?
In C++, both set and HashSet(also called unordered_set) are used to store elements but they have different properties and use cases. In this article, we will learn the key differences between a set and a HashSet in C++. Set in C++In C++, a std::set is a container that stores only the unique elements
4 min read