在 c++ 中,std::unique_lock
和 std::lock_guard
都属于标准库 <mutex>
中的互斥锁管理工具,用于简化互斥锁的使用并确保线程安全。但它们存在一些显著区别,下面为你详细介绍:
1.自动锁定与解锁机制
1) std::lock_guard
:一个轻量级的互斥锁包装器,采用了 raii(资源获取即初始化)技术。当 std::lock_guard
对象被创建时,它会自动锁定所关联的互斥锁;当对象离开其作用域时,会自动解锁该互斥锁。它的设计遵循最小化原则,仅提供最基本的锁管理功能,没有额外的开销。其核心实现原理可以简化为:
template<classmutex> classlock_guard { public: explicitlock_guard(mutex& m) : mutex(m) { mutex.lock(); } ~lock_guard() { mutex.unlock(); } lock_guard(const lock_guard&) = delete; lock_guard& operator=(const lock_guard&) = delete; private: mutex& mutex; };
示例代码如下:
#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; // 当 lock_guard 对象离开作用域时,互斥锁会自动解锁 } int main() { std::thread t(printmessage); t.join(); return 0; }
2) std::unique_lock
: 同样基于 raii 技术,在对象销毁时会自动解锁互斥锁。不过,它的锁定和解锁操作更加灵活,可以在对象创建时选择不立即锁定互斥锁,也可以在对象生命周期内手动锁定和解锁。unique_lock 是 c++11 标准中引入的更高级的锁管理器,设计目标是提供更灵活的锁管理能力。其核心接口包括:
class unique_lock { public: // 构造时可选立即加锁、延迟加锁或尝试加锁 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); // 转移构造函数 unique_lock(unique_lock&& other) noexcept; // 手动控制接口 voidlock(); booltry_lock(); voidunlock(); // 状态查询 explicitoperatorbool()constnoexcept; boolowns_lock()constnoexcept; };
示例代码如下:
#include <iostream> #include <mutex> #include <thread> std::mutex mtx; void printmessage() { std::unique_lock<std::mutex> lock(mtx, std::defer_lock); // 手动锁定互斥锁 lock.lock(); std::cout << "this message is protected by unique_lock." << std::endl; // 手动解锁互斥锁 lock.unlock(); } int main() { std::thread t(printmessage); t.join(); return 0; }
std::unique_lock
允许在对象生命周期内多次手动调用 lock()
、unlock()
和 try_lock()
方法。这在需要在临界区内进行部分操作后暂时释放锁,执行一些非关键操作,然后再次锁定的场景中很有用。如:
#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; // 执行部分临界区操作 lock.unlock(); std::cout << "temporarily released the lock." << std::endl; // 执行一些非关键操作 lock.lock(); std::cout << "re - entered critical section." << std::endl; // 继续执行临界区操作 } int main() { std::thread t(work); t.join(); return 0; }
2.灵活性
1)std::lock_guard: 功能相对单一,缺乏灵活性。一旦创建,就会立即锁定互斥锁,并且在其生命周期内无法手动解锁,只能在对象离开作用域时自动解锁。
2)std::unique_lock:
具有更高的灵活性。它支持三种锁定策略:
std::defer_lock_t // 延迟锁定 std::try_to_lock_t // 尝试锁定 std::adopt_lock_t // 接管已锁定状态
- 延迟锁定(
std::defer_lock
):创建std::unique_lock
对象时,可以选择不立即锁定互斥锁。这在需要先进行一些准备工作,之后再锁定互斥锁的场景中非常有用。如:
#include <iostream> #include <mutex> #include <thread> std::mutex mtx; void work() { std::unique_lock<std::mutex> lock(mtx, std::defer_lock); // 进行一些无需加锁的准备工作 std::cout << "doing some preparation work..." << std::endl; lock.lock(); std::cout << "critical section entered." << std::endl; // 临界区代码 lock.unlock(); std::cout << "critical section exited." << std::endl; } int main() { std::thread t(work); t.join(); return 0; }
- 尝试锁定(
std::try_to_lock
):使用std::try_to_lock
可以尝试锁定互斥锁,如果互斥锁当前已被其他线程锁定,std::unique_lock
对象不会阻塞,而是立即返回,可通过owns_lock()
方法判断是否成功锁定。如:
#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; // 临界区代码 } else { std::cout << "failed to lock the mutex, doing other work." << std::endl; // 执行其他不需要锁定互斥锁的工作 } } int main() { std::thread t(work); t.join(); return 0; }
3.所有权转移
std::lock_guard
:
- 不支持所有权转移,即不能将一个
std::lock_guard
对象的互斥锁所有权转移给另一个对象。
std::unique_lock
:
- 支持所有权转移,可以通过移动构造函数或移动赋值运算符将互斥锁的所有权从一个
std::unique_lock
对象转移到另一个对象。这在函数返回std::unique_lock
对象或需要在不同作用域之间传递锁的所有权时非常有用。
示例代码如下:
#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); t.join(); return 0; }
4.可与条件变量配合使用
std::unique_lock
能够与 std::condition_variable
一起使用,std::condition_variable
的 wait()
、wait_for()
和 wait_until()
等方法要求传入 std::unique_lock
对象,因为在等待期间需要释放和重新获取锁。
#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); cv.wait(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(); t.join(); return 0; }
5.性能开销
std::lock_guard
:
- 由于其功能简单,没有额外的状态管理,因此性能开销相对较小,适合用于简单的锁定场景。
std::unique_lock
:
- 为了支持更多的灵活性,
std::unique_lock
需要维护更多的状态信息,因此性能开销相对较大。在对性能要求极高且锁定逻辑简单的场景下,使用std::lock_guard
更为合适。
综上所述,std::lock_guard
适用于简单的锁定场景,追求简洁性和较低的性能开销;而 std::unique_lock
则适用于需要更复杂锁定逻辑、支持所有权转移的场景,但会带来一定的性能开销。
到此这篇关于c++中unique_lock和lock_guard区别小结的文章就介绍到这了,更多相关c++ unique_lock和lock_guard区别内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论