Open In App

unique_lock or lock_guard: Which Is Better?

Last Updated : 08 May, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

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.


Practice Tags :

Similar Reads