std::lock_guard
和 std::unique_lock
都是 C++ 标准库 <mutex>
中提供的用于管理互斥体(mutex)的 RAII(Resource Acquisition Is Initialization)风格的类。它们的主要目标都是确保互斥体在适当的时候被锁定和解锁,以避免忘记释放锁或多次锁定同一互斥体导致的死锁等问题。然而,它们在功能和使用场景上存在一些差异。
std::lock_guard
std::lock_guard
是一个简单的互斥体包装器,它在构造时锁定互斥体,并在析构时自动解锁。std::lock_guard
不提供手动解锁互斥体的功能,一旦创建,它就会在对象的生命周期内保持锁定状态。
特点:
- 自动锁定和解锁:无需显式调用锁定或解锁函数。
- 不可复制:
std::lock_guard
对象不可复制,但可移动。 - 简单易用:适用于简单的锁定/解锁场景。
示例:
std::mutex mtx;
void safe_increment() { std::lock_guard<std::mutex> guard(mtx); // 临界区,互斥体 mtx 被锁定 // ...
} // 离开作用域时,guard 析构,mtx 自动解锁
std::unique_lock
std::unique_lock
是一个更灵活的互斥体包装器,它提供了更多的控制选项,如手动锁定、解锁、延迟锁定、尝试锁定等。
特点:
- 手动锁定和解锁:可以通过调用
lock()
,unlock()
,try_lock()
等成员函数来控制锁定状态。 - 可延迟锁定:可以在创建
std::unique_lock
对象时不立即锁定互斥体,而是在需要时手动锁定。 - 可与
std::condition_variable
配合使用:std::unique_lock
可以与std::condition_variable
一起使用,实现线程间的条件等待和通知。 - 可复制和移动(但复制后原始对象将不再拥有互斥体的所有权):
std::unique_lock
对象可以复制和移动,但需要注意所有权的变化。
示例:
std::mutex mtx;
std::condition_variable cv;
bool ready = false; void worker_thread() { std::unique_lock<std::mutex> lock(mtx); cv.wait(lock, []{return ready;}); // 等待条件满足,同时释放锁 // ... 执行任务
} void signal_ready() { { std::lock_guard<std::mutex> lock(mtx); ready = true; } // 释放锁 cv.notify_one(); // 通知等待的线程
}
总结
- 如果你只需要简单的锁定/解锁操作,并且确保在离开作用域时自动解锁,那么
std::lock_guard
是更好的选择。 - 如果你需要更多的控制选项,如手动锁定、解锁、延迟锁定、与
std::condition_variable
配合使用等,那么std::unique_lock
是更合适的选择。
白话
std::lock_guard在析构的时候,指定解锁,想提前解锁也解锁不了。而std::unique_lock可以随时控制解锁。可以理解为std::lock_guard是一个std::unique_lock的简化版本,虽然不够灵活但可以覆盖大部分使用场景,且简单、安全、有效