在现代 C++ 多线程编程中,锁机制是确保线程安全访问共享资源的关键手段。C++ 标准库为开发者提供了多种锁管理工具,其中最常用的是 std::lock_guard
和 std::unique_lock
。这两个类都用于管理互斥锁(std::mutex
),以确保多个线程对共享资源的同步访问,但它们在使用场景和灵活性方面存在显著的差异。
一、什么是 std::lock_guard
?
std::lock_guard
是 C++ 标准库中的一个轻量级工具,用于在一个作用域中管理互斥锁的生命周期。它在构造时自动锁定互斥锁,在销毁时自动解锁。这个工具的最大优势在于其简洁性和自动化管理,使得锁定和解锁的过程更加安全,避免了常见的手动锁管理错误。
1.1 std::lock_guard
的基本用法
|
|
在这个例子中,std::lock_guard
确保了在函数结束时,无论是正常退出还是因为异常提前退出,互斥锁都能够自动解锁。这种方式减少了手动管理锁的复杂性,确保线程安全。
1.2 std::lock_guard
的主要特点
- 简单高效:一旦创建,锁定和解锁是自动化的,使用非常方便。
- 不可移动、不可复制:它的生命周期严格绑定到作用域,不能被移动或复制。
- 无手动解锁功能:一旦创建后,
std::lock_guard
将一直持有锁,直到作用域结束时自动释放。
二、什么是 std::unique_lock
?
std::unique_lock
是 C++ 标准库中更灵活的锁管理器,提供了对互斥锁的手动控制,包括延迟锁定、手动解锁和重新锁定的功能。与 std::lock_guard
相比,std::unique_lock
的灵活性使得它能够应对更复杂的线程同步场景,特别是在与条件变量(std::condition_variable
)一起使用时。
2.1 std::unique_lock
的基本用法
|
|
在这个示例中,std::unique_lock
提供了手动解锁和重新锁定的功能,使得开发者可以灵活地控制锁的持有时间和范围。这在处理复杂同步逻辑时非常有用。
2.2 std::unique_lock
的主要特点
- 延迟锁定:可以在构造时选择不锁定互斥锁,后续手动锁定。
- 手动解锁:提供了
unlock()
方法,可以在需要时手动释放锁。 - 重新锁定:可以通过
lock()
方法在需要时重新获得锁。 - 可移动:
std::unique_lock
支持移动语义,允许在不同的作用域之间传递或存储在容器中。
三、std::lock_guard
与 std::unique_lock
的区别
尽管 std::lock_guard
和 std::unique_lock
都是用于管理互斥锁的工具,但它们在灵活性和功能上有很大不同:
特性 | std::lock_guard |
std::unique_lock |
---|---|---|
锁定时机 | 构造时立即锁定 | 可延迟锁定 |
解锁方式 | 作用域结束时自动解锁 | 可手动解锁,也可在作用域结束时解锁 |
可移动性 | 不可移动 | 可移动 |
灵活性 | 简单,不灵活 | 更灵活,支持手动锁定和解锁 |
适用条件变量 | 不支持 | 支持,与条件变量一起使用 |
四、为什么条件变量需要 std::unique_lock
条件变量(std::condition_variable
)是多线程编程中的一种常见同步机制,用于让线程在等待某个条件时进入等待状态,并在条件满足时继续执行。为了正确使用条件变量,需要能暂时释放互斥锁,以便其他线程能够修改条件。因此,必须使用 std::unique_lock
而不能使用 std::lock_guard
,原因如下:
-
条件变量需要释放锁:在调用
wait()
时,条件变量会临时释放锁以允许其他线程获取互斥锁并改变条件。std::lock_guard
无法手动释放锁,因此无法与条件变量一起使用,而std::unique_lock
提供了这种灵活性。 -
复杂的同步逻辑:条件变量通常用于实现复杂的线程同步逻辑,
std::unique_lock
的手动锁定、解锁功能使得它可以应对这些复杂场景。
|
|
在这个例子中,worker_thread
使用 std::unique_lock
与条件变量一起工作。当 cv.wait()
被调用时,它会释放锁并等待条件变量的通知。set_ready
线程通过 notify_one()
通知条件变量,唤醒等待中的线程。
五、总结
-
std::lock_guard
提供了一种简洁的方式来管理互斥锁,适合用于简单的锁定场景。它的生命周期严格绑定到作用域,使用非常安全,但不适合处理需要手动控制锁的复杂场景。 -
std::unique_lock
提供了更大的灵活性,允许开发者手动锁定和解锁互斥锁,适合于复杂的多线程场景,特别是在需要条件变量时使用。