几种锁的对比
几种锁的对比
好的,那我来详细对比一下 std::lock_guard 和 std::unique_lock 这两种常用的锁管理方式,以及它们与直接使用锁的区别。
1. std::lock_guard
定义 :
std::lock_guard是一个简单的锁管理器,用于管理互斥锁的锁定和解锁。它在构造时锁定互斥锁,在析构时自动释放锁。特点 :
- 自动管理 :构造函数锁定锁,析构函数释放锁,确保锁的管理不会被遗忘。
- 不可移动、不可复制 :
std::lock_guard对象不能被移动或复制,这意味着锁不能在不同的作用域之间传递。 - 简单易用 :适用于简单的锁需求场景,不需要复杂的锁管理。
- 作用域绑定 :锁的生命周期与
std::lock_guard对象的作用域绑定,一旦超出作用域,锁自动释放。
示例 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
std::mutex mtx;
void print_block(int n) {
std::lock_guard<std::mutex> guard(mtx);
for (int i = 0; i < 5; ++i) {
std::cout << "Thread " << n << ": " << i << '\n';
}
}
int main() {
std::thread a(print_block, 1);
std::thread b(print_block, 2);
a.join();
b.join();
return 0;
}
2. std::unique_lock
定义 :
std::unique_lock是一个更灵活的锁管理器,提供了比std::lock_guard更多的功能。它也用于管理互斥锁的锁定和解锁。特点 :
- 延迟锁定 :可以在构造时不锁定锁,之后通过调用
lock()方法显式锁定。 - 条件变量支持 :与条件变量配合使用时,
std::unique_lock可以在等待条件变量时释放锁,并在条件满足时重新获取锁。 - 可移动 :支持移动操作,允许锁在不同的作用域之间传递。
- 作用域绑定 :与
std::lock_guard类似,锁的生命周期与std::unique_lock对象的作用域绑定,超出作用域时自动释放锁。
- 延迟锁定 :可以在构造时不锁定锁,之后通过调用
示例 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void print_id(int id) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [] { return ready; });
std::cout << "Thread " << id << '\n';
}
int main() {
std::thread t1(print_id, 1);
std::thread t2(print_id, 2);
std::this_thread::sleep_for(std::chrono::seconds(1));
{
std::lock_guard<std::mutex> guard(mtx);
ready = true;
}
cv.notify_all();
t1.join();
t2.join();
return 0;
}
3. 直接使用锁
定义 :直接使用互斥锁(如
std::mutex)进行锁定和解锁。特点 :
- 手动管理 :需要手动调用
lock()和unlock()方法,容易忘记释放锁,导致死锁。 - 灵活性高 :提供完整的控制权,可以实现复杂的锁逻辑。
- 易出错 :手动管理容易出错,如忘记解锁、重复解锁等。
- 手动管理 :需要手动调用
示例 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
std::mutex mtx;
void print_block(int n) {
mtx.lock();
for (int i = 0; i < 5; ++i) {
std::cout << "Thread " << n << ": " << i << '\n';
}
mtx.unlock();
}
int main() {
std::thread a(print_block, 1);
std::thread b(print_block, 2);
a.join();
b.join();
return 0;
}
对比总结
| 特性 | std::lock_guard |
std::unique_lock |
直接使用锁 |
|---|---|---|---|
| 锁定时机 | 构造时立即锁定 | 可延迟锁定 | 手动锁定 |
| 解锁时机 | 析构时自动解锁 | 析构时自动解锁 | 手动解锁 |
| 可移动 | 否 | 是 | 否 |
| 条件变量支持 | 否 | 是 | 否 |
| 适用场景 | 简单的锁需求,不需要延迟锁定或条件变量支持 | 复杂的锁需求,需要延迟锁定或条件变量支持 | 需要完全控制锁的锁定和解锁时机 |
| 安全性 | 高,自动管理锁的生命周期 | 高,自动管理锁的生命周期 | 低,容易忘记解锁或重复解锁 |
| 灵活性 | 低,只能构造时锁定,析构时解锁 | 高,支持延迟锁定、条件变量等待等 | 高,完全手动控制 |
总结
std::lock_guard:适用于简单的锁需求场景,自动管理锁的生命周期,确保锁的正确释放。std::unique_lock:适用于复杂的锁需求场景,如延迟锁定、条件变量等待等,提供更高的灵活性。- 直接使用锁 :适用于需要完全控制锁的锁定和解锁时机的场景,但容易出错,需要谨慎使用。
在实际开发中,推荐优先使用 std::lock_guard 或 std::unique_lock 来管理锁,以提高代码的安全性和可维护性。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 贺腾的博客!








