std::unique_lock与std::lock_guard区别

本文详细介绍了C++中lock_guard和unique_lock两种线程锁的使用方式,包括它们的构造函数、析构函数控制锁的作用范围,以及如何通过构造函数延时加锁和手动加锁解锁。同时对比了两种锁的赋值操作和资源消耗,帮助读者理解它们的特性和应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用方式

lock_guard:通过构造函数和析构函数控制锁的作用范围,创造对象的时候加锁,离开作用域的时候解锁;

std::mutex m_mutex;
void print(int cnt)
{
    std::lock_guard<mutex> lock(m_mutex);
    cout << std::this_thread::get_id() << " " << cnt << endl;
}

unique_lock:可以通过构造函数和析构函数控制锁的作用范围,也可以在构造函数中延时加锁,在需要的时候手动加锁和解锁。

std::mutex m_mutex;
void print(int cnt)
{
    //用unique_lock来对锁进行管理.defer_lock是它的构造参数之一.用来推迟加锁.
    unique_lock<mutex> lock(m_mutex, defer_lock);
    /*
        ...这里可以进行一系列线程安全的操作...
    */
    //手动加锁.
    lock.lock();
    cout << std::this_thread::get_id() << " " << cnt << endl;
    lock.unlock();
    //这里用来计算sum的值.然后打印.因为sum是在函数内定义的局部变量.所以下面的代码是线程安全的.没必要用锁对这段代码进行保护.所以在上面用unlock解锁.
    int sum = 0;
    for (int i = 0; i < cnt; ++i)
    {
        sum += i;
    }
    //最后在手动加锁.不用手动释放.因为会在生命周期结束时自动释放.
    lock.lock();
    cout << std::this_thread::get_id() << " " << sum << endl;
}

赋值操作

lock_guard不支持赋值操作。unique_lock支持可移动赋值,若占有互斥量则解锁互斥,并取得另一者的所有权

资源消耗

unique_lock相对而言更加灵活,对于资源的消耗明显要大一些,因为它要维持mutex的状态;
lock_guard虽然笨重一些,但是资源消耗相对要小一点。

### C++ 中 `std::unique_lock` 的用法 #### 定义基本功能 `std::unique_lock` 是一种可选锁定的互斥锁类模板,允许延迟加锁和解锁操作。这使得它比 `std::lock_guard` 更灵活,在某些情况下也更复杂[^1]。 ```cpp #include <mutex> std::mutex mtx; std::unique_lock<std::mutex> lck(mtx); ``` 上述代码展示了如何创建一个基于标准互斥量 (`std::mutex`) 的 `std::unique_lock` 实例,并立即对其进行锁定。当作用域结束时,该锁会自动释放。 #### 延迟锁定 除了构造函数中的即时锁定外,还可以通过调用成员方法来进行手动控制: - 使用 `try_lock()` 尝试获取锁而不阻塞; - 调用 `lock()` 显式请求独占访问权限; - 执行 `unlock()` 提前解除当前持有的锁。 这些特性赋予了开发者更大的灵活性去设计复杂的同步逻辑[^3]。 #### 条件变量配合使用 另一个重要应用场景是在条件等待模式下作为参数传递给 `std::condition_variable` 或者其他类似的设施: ```cpp void wait_for_data(std::condition_variable& cv, std::mutex& m, bool& ready){ std::unique_lock<std::mutex> lk(m); // 构造并持有锁 while (!ready){ // 循环检查状态 cv.wait(lk); // 等待通知的同时保持锁定 } } ``` 此片段说明了如何利用 `std::unique_lock` 结合条件变量实现高效的生产者消费者模型。 #### 错误处理异常安全性 值得注意的是,如果在拥有 `std::unique_lock` 对象期间抛出了未捕获的异常,则析构函数仍然会被正常调用来确保资源被正确清理——即使程序流因错误而中断[^2]。 #### 性能考量 尽管提供了额外的功能选项,但在仅需简单保护临界区的情况下,应优先考虑性能更高的替代方案如 `std::lock_guard`;因为后者实现了所谓的“零开销抽象”,即编译器可以在优化阶段完全消除其运行时期成本。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值