【C++11】std::unique_lock<std::mutex> 和 std::lock_guard<std::mutex>
·
std::unique_lock 和 std::lock_guard 都是 C++ 中的互斥锁包装器,但它们有不同的特性和使用场景。
1. 基本区别
| 特性 | std::lock_guard |
std::unique_lock |
|---|---|---|
| 灵活性 | 简单,功能有限 | 灵活,功能丰富 |
| 锁管理 | RAII,自动锁/解锁 | 手动控制锁/解锁 |
| 性能 | 更轻量,开销小 | 稍重,有额外开销 |
| 条件变量 | 不能配合使用 | 必须配合使用 |
| 移动语义 | 不支持 | 支持 |
| 锁策略 | 有限支持 | 丰富支持 |
2. 代码示例对比
std::lock_guard - 简单自动管理
#include <mutex>
std::mutex mutex;
void simple_function() {
{
std::lock_guard<std::mutex> lock(mutex); // 构造时自动加锁
// 临界区代码
// ...
} // 析构时自动解锁
// 锁已自动释放
}
std::unique_lock - 灵活手动控制
#include <mutex>
std::mutex mutex;
void flexible_function() {
std::unique_lock<std::mutex> lock(mutex); // 构造时自动加锁
// 可以手动解锁
lock.unlock();
// 执行一些非临界区代码
// ...
// 可以重新加锁
lock.lock();
// 更多临界区代码
// ...
} // 如果锁仍被持有,析构时自动解锁
3. 条件变量必须使用 std::unique_lock
#include <mutex>
#include <condition_variable>
std::mutex mutex;
std::condition_variable cv;
bool data_ready = false;
void consumer() {
std::unique_lock<std::mutex> lock(mutex); // 必须用 unique_lock
// wait() 会临时解锁互斥量,并在唤醒时重新加锁
cv.wait(lock, []{ return data_ready; });
// 处理数据...
}
void producer() {
{
std::lock_guard<std::mutex> lock(mutex); // 这里可以用 lock_guard
data_ready = true;
}
cv.notify_one();
}
4. 延迟锁定的例子
#include <mutex>
std::mutex mutex;
void deferred_locking() {
// 创建但不立即加锁
std::unique_lock<std::mutex> lock(mutex, std::defer_lock);
// 执行一些不需要锁的操作
// ...
// 在需要的时候手动加锁
lock.lock();
// 临界区代码
// ...
// 可以手动解锁
lock.unlock();
// 更多非临界区代码
// ...
// 如果需要,可以重新加锁
if (some_condition) {
lock.lock();
// ...
}
} // 自动管理最终状态
5. 尝试加锁的场景
#include <mutex>
#include <chrono>
std::mutex mutex;
void try_lock_examples() {
// 尝试立即加锁
std::unique_lock<std::mutex> lock(mutex, std::try_to_lock);
if (lock.owns_lock()) {
// 成功获取锁
// ...
} else {
// 锁被其他线程持有,执行替代方案
// ...
}
}
void timed_lock_example() {
std::unique_lock<std::mutex> lock(mutex, std::defer_lock);
// 尝试在100ms内获取锁
if (lock.try_lock_for(std::chrono::milliseconds(100))) {
// 成功获取锁
// ...
} else {
// 超时,执行替代方案
// ...
}
}
6. 移动语义支持
#include <mutex>
#include <vector>
std::mutex global_mutex;
std::unique_lock<std::mutex> get_lock() {
std::unique_lock<std::mutex> lock(global_mutex);
// 一些初始化...
return lock; // 可以返回,支持移动语义
}
void move_semantics_example() {
// 从函数接收锁
std::unique_lock<std::mutex> lock = get_lock();
// 锁的所有权可以转移
std::vector<std::unique_lock<std::mutex>> lock_pool;
lock_pool.push_back(std::move(lock));
// 现在 lock 不再拥有锁的所有权
}
7. 实际应用场景对比
适合 std::lock_guard 的场景:
void simple_critical_section() {
std::lock_guard<std::mutex> lock(mutex); // 简单直接
// 执行一些操作
// 不需要条件变量
// 不需要手动控制锁
}
适合 std::unique_lock 的场景:
void complex_critical_section() {
std::unique_lock<std::mutex> lock(mutex);
// 阶段1:需要锁的操作
// ...
// 临时释放锁进行IO操作
lock.unlock();
perform_expensive_io();
lock.lock();
// 阶段2:又需要锁的操作
// ...
// 使用条件变量等待
cv.wait(lock, []{ return condition; });
// 阶段3:最终操作
// ...
}
8. 性能考虑
#include <mutex>
class PerformanceSensitiveClass {
private:
std::mutex mutex_;
int data_;
public:
// 在简单场景中使用 lock_guard(性能更好)
void simple_increment() {
std::lock_guard<std::mutex> lock(mutex_);
++data_;
}
// 在复杂场景中使用 unique_lock(功能更强)
void complex_operation() {
std::unique_lock<std::mutex> lock(mutex_);
if (data_ > 100) {
lock.unlock();
process_large_data();
lock.lock();
}
// 更多操作...
}
};
9. 锁策略示例
void lock_strategies() {
std::mutex mutex;
// 1. 默认:立即加锁
std::unique_lock<std::mutex> lock1(mutex);
// 2. 延迟加锁
std::unique_lock<std::mutex> lock2(mutex, std::defer_lock);
// 3. 尝试加锁
std::unique_lock<std::mutex> lock3(mutex, std::try_to_lock);
// 4. 接受已拥有的锁(adopt_lock)
mutex.lock();
std::unique_lock<std::mutex> lock4(mutex, std::adopt_lock);
}
总结
使用 std::lock_guard 当:
- 简单的 RAII 锁管理
- 整个作用域都需要锁
- 不需要条件变量
- 性能是关键考虑
使用 std::unique_lock 当:
- 需要配合条件变量
- 需要手动控制锁的时机
- 需要尝试加锁或超时加锁
- 需要转移锁的所有权
- 需要更复杂的锁策略
在大多数简单情况下,std::lock_guard 就足够了,而且性能更好。只有在需要更高级功能时才使用 std::unique_lock。
更多推荐


所有评论(0)