C++ std::unique_lock 用法

2023-09-21 15:46:21


std::unique_lock 是 C++11 提供的一个用于管理互斥锁的类,它提供了更灵活的锁管理功能,适用于各种多线程场景。

1.创建 std::unique_lock 对象

std::unique_lock<std::mutex> lock(mutex); // 创建 std::unique_lock 并关联互斥锁 mutex

你可以在构造函数中传入一个互斥锁(std::mutex 或其它互斥锁类型)来创建 std::unique_lock 对象,并且会在构造时获取互斥锁的所有权。此时,互斥锁被锁住,其他线程无法获得锁。

2.自动加锁和解锁

{
    std::unique_lock<std::mutex> lock(mutex); // 自动加锁
    // 临界区代码
} // 自动解锁

使用 std::unique_lock 创建的对象,当其生命周期结束时(通常是在大括号的作用域结束时),会自动解锁互斥锁,以确保互斥锁在不再需要时被释放。

3.延迟加锁与手动加解锁

std::unique_lock 还支持在初始化时不立即加锁,而是在需要时延迟加锁。这种特性对于一些多线程场景非常有用,允许你在获得锁之前执行一些非临界区的操作,从而减少锁的持有时间。

创建 std::unique_lock 对象时,传入互斥锁但不加锁:

std::unique_lock<std::mutex> lock(mutex, std::defer_lock);

在需要时手动加锁:

lock.lock();   // 手动加锁
// 临界区代码
lock.unlock(); // 手动解锁

你可以使用 lock() 手动加锁互斥锁,然后在互斥锁保护的临界区内执行代码,最后使用 unlock() 手动解锁互斥锁。这种方式可以让你更灵活地控制锁的生命周期。

4.尝试加锁

std::unique_lock 还提供了 try_lock() 方法,用于尝试加锁,如果锁不可用,则返回 false,如果锁成功获取,则返回 true。

std::unique_lock<std::mutex> lock(mutex, std::defer_lock);
if (lock.try_lock()) {
    // 锁成功获取,执行临界区代码
    lock.unlock();
} else {
    // 锁不可用,执行其他逻辑
}

5.配合条件变量使用

condition_variable(条件变量)是 C++11 中提供的一种多线程同步机制,它允许一个或多个线程等待另一个线程发出的通知,以便能够有效地进行线程同步。

条件变量(std::condition_variable)需要与 std::unique_lock 一起使用,以实现线程的等待和通知机制。

std::unique_lock<std::mutex> lck(mutex);
while (!condition) {
    conditionVariable.wait(lock); // 等待条件满足并释放锁
}
// 条件满足,重新获取锁并继续执行

条件变量的成员函数 wait() 会在阻塞线程的那一刻(当线程被添加到等待队列中时),函数会自动调用 lck.unlock() 释放锁,允许其他锁定的线程继续执行。

一旦收到唤醒通知(由其他线程调用 notify_one() 或 notify_all() 通知),该函数就会解除阻塞并调用 lck.lock(),使 lck 处于与调用该函数时相同的状态,然后函数返回。请注意,返回前调用 lck.lock() 加锁可能会再次阻塞线程。

为什么条件变量需要互斥锁的配合呢?

因为 condition 和等待队列都是多线程的共享资源,当访问这些共享资源时需要互斥访问。

6.小结

std::unique_lock 提供了对互斥锁更高级别的控制和灵活性,使得多线程编程更加安全和容易。在多数情况下,推荐使用 std::unique_lock 而不是直接操作互斥锁,因为它能够自动管理锁的生命周期,减少了出错的机会。


参考文献

std::unique_lock - cplusplus.com
std::condition_variable - cplusplus.com

更多推荐

ReclerView的多种条目布局

一、效果图模仿QQ看点的布局:二、代码MoreTypeAdapter:packagecom.example.qq.ThirdFragment.Adapter;importandroid.content.Context;importandroid.view.View;importandroid.view.ViewGrou

肖sir__项目实战讲解__004

项目实战讲解一、项目的类型金融类:保险(健康险+理财险)、证券、基金(股票型基金、混合型基金、指数型基金、债券型基金、天天基金网(ETF基金、货币型基金、量化基金)、银行、贷款、信用卡、外汇、二元期权、期货原油、blockchain、数字货币、黄金白银、期货、理财P2P(平安陆金所)、比特币。金融类中贷款:平安普惠、3

MySQL 索引

文章目录1.什么是索引?2.为什么使用索引?3.创建索引4.查看索引5.修改索引6.删除索引参考文献数据库索引是MySQL中提高查询性能和数据检索速度的关键工具之一。本文将介绍MySQL索引的基本概念,如何创建索引,以及如何使用索引来优化数据库查询。1.什么是索引?数据库索引是一种数据结构,用于加速数据库查询操作。它是

软件定制APP开发步骤分析|小程序

软件定制APP开发步骤分析|小程序软件定制开发步骤:1.需求分析:这是软件定制开发的第一步,也是最关键的一步。在这个阶段,软件开发团队需要与客户进行沟通,了解客户的具体需求和期望。通过讨论和交流,确定软件的功能和特性,制定开发计划和时间表。2.设计阶段:在需求分析的基础上,软件开发团队需要进行软件的设计。这个阶段包括对

Ceph入门到精通-ceph pool 删除导致 misplaced 的原因

misplaced的原因Ceph中的misplaced对象是指将对象(或对象的副本)存储在错误的位置上,这可能会导致性能下降或数据不一致的问题。在删除Ceph池时,可能会导致misplaced的原因有以下几个:删除过程中的操作失误:在删除Ceph池时,操作人员可能会不小心删除了正在使用的池,导致对象被误删除或移动到错误

springboot和vue:四、web入门(静态资源访问+文件上传+拦截器)

静态资源访问使用IDEA创建SpringBoot项目,会默认创建出classpath:/static/目录,静态资源一般放在这个目录下即可。如果默认的静态资源过滤策略不能满足开发需求,也可以自定义静态资源过滤策略。在application.properties中定义过滤规则和静态资源位置。过滤规则为/static/**

ESP8266 WiFi物联网智能插座—项目简介

目录1、项目背景2、设备节点功能3、上位机功能物联网虽然能够使家居设备和系统实现自动化、智能化管理,但是依然需要依靠更为先进的终端插座作为根本保障,插座是所有家用电器需要使用的电源设备,插座的有序智能管理,对于实现智能家居设备的统一智能管理具有举足轻重的作用。无论是家庭生活,还是工业制造,插座在生活中的应用无所不在,当

GPIO子系统编写LED灯的驱动、linux内核定时器

一、GPIO子系统1.概念:一个芯片厂商生产出芯片后会给linux提供一个当前芯片中gpio外设的驱动,我们当前只需要调用对应的厂商驱动即可完成硬件的控制。而linux内核源码中的gpio厂商驱动有很多,这里linux内核对厂商驱动做了一些封装,提供了一系列的API,我们在自己编写的设备驱动中只需要调用这些API即可访

AI定义汽车,长城画了个看得见的“饼”

何小鹏提出的“AI定义汽车”概念,正在被业界关注并重视,这其中,就包括长城汽车。9月12日,在一场媒体交流会上,长城汽车透露,其内部已经成立了一个AILab部门,主要的工作,就是为整个长城提供包括产品、技术以及企业产品开发在内的人工智能大模型技术底座。据了解,该部门的技术负责人,是原沙龙品牌智能化技术中心负责人杨继峰。

MySQL 学习笔记(基础)

首先解释数据库DataBase(DB):即存储数据的仓库,数据经过有组织的存储数据库管理系统DataBaseManagementSystem(DBMS):管理数据库的软件SQL(StructuredQueryLanguage):结构化查询语言/操作关系型数据库的编程语言/定义操作所以关系型数据库的统一标准关系型数据库下

2716. 最小化字符串长度

2716.最小化字符串长度给你一个下标从0开始的字符串s,重复执行下述操作任意次:在字符串中选出一个下标i,并使c为字符串下标i处的字符。并在i左侧(如果有)和右侧(如果有)各删除一个距离i最近的字符c。请你通过执行上述操作任意次,使s的长度最小化。返回一个表示最小化字符串的长度的整数。示例1:输入:s=“aaabc”

热文推荐