In C++,std::unique_lock
andstd::lock_guard
All belong to the standard library<mutex>
The mutex management tool in simplifies the use of mutexes and ensures thread safety. But there are some significant differences, and the following is a detailed introduction to you:
1. Automatic locking and unlocking mechanism
1) std::lock_guard
: A lightweight mutex wrapper that uses RAII (resource acquisition, initialization) technology. whenstd::lock_guard
When an object is created, it will automatically lock the associated mutex; when the object leaves its scope, it will automatically unlock the mutex. Its design follows the principle of minimization and provides only the most basic lock management function without additional overhead. Its core implementation principle can be simplified to:
template<classMutex> classlock_guard { public: explicitlock_guard(Mutex& m) : mutex(m) { (); } ~lock_guard() { (); } lock_guard(const lock_guard&) = delete; lock_guard& operator=(const lock_guard&) = delete; private: Mutex& mutex; };
The sample code is as follows:
#include <iostream> #include <mutex> #include <thread> std::mutex mtx; void printMessage() { std::lock_guard<std::mutex> lock(mtx); std::cout << "This message is protected by lock_guard." << std::endl; // When the lock_guard object leaves scope, the mutex will be automatically unlocked} int main() { std::thread t(printMessage); (); return 0; }
2) std::unique_lock
: Also based on RAII technology, the mutex lock will be automatically unlocked when the object is destroyed. However, its locking and unlocking operations are more flexible, allowing you to choose not to lock the mutex immediately when object creation, or manually lock and unlock during the life of the object. unique_lock is a more advanced lock manager introduced in the C++11 standard, with the design goal being to provide more flexible lock management capabilities. Its core interfaces include:
class unique_lock { public: // When constructing, you can choose to lock immediately, delay locking or try to lock unique_lock(mutex_type& m, std::defer_lock_t) noexcept; unique_lock(mutex_type& m, std::try_to_lock_t); unique_lock(mutex_type& m, std::adopt_lock_t); // Transfer constructor unique_lock(unique_lock&& other) noexcept; // Manual control interface voidlock(); booltry_lock(); voidunlock(); // Status query explicitoperatorbool()constnoexcept; boolowns_lock()constnoexcept; };
The sample code is as follows:
#include <iostream> #include <mutex> #include <thread> std::mutex mtx; void printMessage() { std::unique_lock<std::mutex> lock(mtx, std::defer_lock); // Manually lock the mutex lock (); std::cout << "This message is protected by unique_lock." << std::endl; // Manually unlock the mutex lock (); } int main() { std::thread t(printMessage); (); return 0; }
std::unique_lock
Allows multiple manual calls within the life cycle of the objectlock()
、unlock()
andtry_lock()
method. This is useful in scenarios where a lock is temporarily released after partial operations within the critical zone, perform some non-critical operations, and then lock it again. like:
#include <iostream> #include <mutex> #include <thread> std::mutex mtx; void work() { std::unique_lock<std::mutex> lock(mtx); std::cout << "Entered critical section." << std::endl; // Perform some critical area operations (); std::cout << "Temporarily released the lock." << std::endl; // Perform some non-critical operations (); std::cout << "Re - entered critical section." << std::endl; // Continue to perform critical area operations} int main() { std::thread t(work); (); return 0; }
2. Flexibility
1) std::lock_guard: The function is relatively single and lacks flexibility. Once created, the mutex is locked immediately and cannot be unlocked manually during its life cycle, and can only be automatically unlocked when the object leaves scope.
2)std::unique_lock:
More flexibility. It supports three locking strategies:
std::defer_lock_t // Delay lockstd::try_to_lock_t // Try to lockstd::adopt_lock_t // Takeover locked
- Delay locking (
std::defer_lock
): Createstd::unique_lock
When an object is used, you can choose not to lock the mutex immediately. This is very useful in scenarios where some preparation is required before locking the mutex. like:
#include <iostream> #include <mutex> #include <thread> std::mutex mtx; void work() { std::unique_lock<std::mutex> lock(mtx, std::defer_lock); // Do some preparations without locking std::cout << "Doing some preparation work..." << std::endl; (); std::cout << "Critical section entered." << std::endl; // Critical area code (); std::cout << "Critical section exited." << std::endl; } int main() { std::thread t(work); (); return 0; }
- Try locking (
std::try_to_lock
):usestd::try_to_lock
You can try to lock the mutex if the mutex is currently locked by another thread.std::unique_lock
The object will not block, but will return immediately and can be passedowns_lock()
Method to determine whether the lock is successful. like:
#include <iostream> #include <mutex> #include <thread> std::mutex mtx; void work() { std::unique_lock<std::mutex> lock(mtx, std::try_to_lock); if (lock.owns_lock()) { std::cout << "Successfully locked the mutex." << std::endl; // Critical area code } else { std::cout << "Failed to lock the mutex, doing other work." << std::endl; // Perform other tasks that do not require locking mutex locks } } int main() { std::thread t(work); (); return 0; }
3. Transfer of ownership
std::lock_guard
:
- It does not support ownership transfer, that is, one cannot be transferred
std::lock_guard
The mutex ownership of the object is transferred to another object.
std::unique_lock
:
- Supports ownership transfer, and ownership of a mutex can be transferred from one by moving constructor or moving assignment operator.
std::unique_lock
Transfer the object to another object. This returns in the functionstd::unique_lock
It is useful when an object or needs to pass ownership of a lock between different scopes.
The sample code is as follows:
#include <iostream> #include <mutex> #include <thread> std::mutex mtx; std::unique_lock<std::mutex> getLock() { std::unique_lock<std::mutex> lock(mtx); return std::move(lock); } void printMessage() { std::unique_lock<std::mutex> lock = getLock(); std::cout << "This message is protected by transferred unique_lock." << std::endl; } int main() { std::thread t(printMessage); (); return 0; }
4. Can be used in conjunction with condition variables
std::unique_lock
Able tostd::condition_variable
Use together,std::condition_variable
ofwait()
、wait_for()
andwait_until()
Such methods require incomingstd::unique_lock
Object, because the lock needs to be released and reacquisitioned during waiting.
#include <iostream> #include <mutex> #include <condition_variable> #include <thread> std::mutex mtx; std::condition_variable cv; bool ready = false; void worker() { std::unique_lock<std::mutex> lock(mtx); (lock, []{ return ready; }); std::cout << "Worker thread is working." << std::endl; } int main() { std::thread t(worker); { std::lock_guard<std::mutex> lock(mtx); ready = true; } cv.notify_one(); (); return 0; }
5. Performance overhead
std::lock_guard
:
- Due to its simple functionality and no additional state management, the performance overhead is relatively small and suitable for simple locking scenarios.
std::unique_lock
:
- To support more flexibility,
std::unique_lock
More status information is required, so the performance overhead is relatively large. In scenarios with extremely high performance requirements and simple locking logic, usestd::lock_guard
More suitable.
In summary,std::lock_guard
Suitable for simple locking scenarios, pursuing simplicity and low performance overhead;std::unique_lock
It is suitable for scenarios where more complex locking logic is required and ownership transfer is supported, but it will bring certain performance overhead.
This is the end of this article about the difference between unique_lock and lock_guard in C++. For more related contents of unique_lock and lock_guard, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!