菜鸟笔记
提升您的技术认知

std::unique_lock和std::lock_guard你会用吗

std::unique_lock 是 C++ 标准库中提供的一个互斥量封装类,用于实现互斥访问和线程同步。它提供了更灵活的锁定和解锁机制,可以适应不同的场景和需求。

std::unique_lock 可以与 std::mutex 或其他可锁定的互斥量一起使用。以下是 std::unique_lock 的基本用法:

  1. 创建 std::mutex 对象或其他可锁定的互斥量。
std::mutex mutex;
  1. 使用 std::unique_lock 来锁定互斥量。
std::unique_lock<std::mutex> lock(mutex);

构造函数中传入互斥量对象 mutex,它将自动对互斥量进行加锁操作。

  1. 执行受保护的代码块。
    在 std::unique_lock 对象创建后,你可以在其范围内编写需要互斥访问的代码。
// 互斥访问的代码块
// ...
  1. 自动解锁。
    std::unique_lock 对象 lock 在其范围结束后(即超出其作用域)将自动对互斥量进行解锁操作。这意味着你无需手动调用解锁函数。

下面是一个简单的示例,演示了如何使用 std::unique_lock 来保护共享资源的访问:

#include <iostream>
#include <mutex>
#include <thread>

std::mutex mutex;
int sharedVariable = 0;

void increment() {
    std::unique_lock<std::mutex> lock(mutex);
    // 对共享资源进行互斥访问
    ++sharedVariable;
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);

    t1.join();
    t2.join();

    std::cout << "共享变量的值:" << sharedVariable << std::endl;

    return 0;
}

在上述示例中,std::unique_lock 对象 lock 用于锁定互斥量 mutex,以保护对 sharedVariable 共享变量的访问。increment 函数在互斥访问的范围内对 sharedVariable 进行递增操作。两个线程通过调用 increment 函数并发地修改共享变量,但由于互斥量的保护,保证了线程安全性。

请注意,与 std::lock_guard 相比,std::unique_lock 提供了更多的灵活性,例如在锁定期间手动释放锁定、在构造函数中延迟锁定等。这使得 std::unique_lock 更适用于某些特定的情况,如条件变量和递归锁定等

本质区别
std::lock_guard 和 std::unique_lock 都是 C++ 标准库中用于管理互斥量的类,它们之间的本质区别如下:

所有权:
std::lock_guard:拥有互斥量的自动锁定权。一旦创建 std::lock_guard 对象,它将对互斥量进行锁定,并在其作用域结束时自动释放锁定。
std::unique_lock:拥有互斥量的手动锁定权。可以在构造函数中选择是否立即锁定互斥量,并可以在其作用域内手动控制锁定和解锁的时机。
灵活性:
std::lock_guard:提供了一种简单的、固定的锁定机制,不支持手动解锁。在 std::lock_guard 对象的作用域内,互斥量将一直保持锁定状态,直到作用域结束。
std::unique_lock:提供了更大的灵活性。可以在构造函数中选择是否立即锁定互斥量,还可以手动调用 lock() 和 unlock() 函数来控制锁定和解锁的时机。
条件变量的支持:
std::lock_guard:不支持与条件变量一起使用。由于 std::lock_guard 无法手动解锁,因此无法满足条件变量等待和通知的需求。
std::unique_lock:支持与条件变量一起使用。通过手动调用 unlock() 和 lock() 函数,可以在适当的时机解锁和重新锁定互斥量,以与条件变量协同工作。
总的来说,std::lock_guard 提供了一种简单的、固定的锁定机制,适用于大多数情况下简单的互斥访问。而 std::unique_lock 提供了更大的灵活性和更多的功能,例如手动控制锁定和解锁的时机,以及与条件变量的配合使用。因此,在需要更高级的互斥控制或与条件变量一起使用时,std::unique_lock 是更适合的选择。