• unique_lock 取代lock_quard
  • unique_lock 的第二个参数
    • std::adopt_lock
    • std::try_to_lock
    • std::defer_lock
  • unique_lock的成员函数
    • lock()
    • unlock()
    • try_to_lock()
    • release()
  • unique_lock所有权的传递

unique_lock 取代lock_guard

应用场景:两个线程A、B,其中A对队列添加元素,B移除元素。

unique_lock是个类模板,工作中,一般使用lock_gard(推荐使用),取代mutex的lock()和unlock()。unique_lock比lock_gard灵活许多,但是unique_lock效率差一点,并且内存占用多一点。

std::unique_lock<std::mutex> sbgard(mu_mutex);

unique_lock 的第二个参数

unique_lock比较灵活,主要是因为这个第二个参数。 lock_guard可以带第二个参数。

std::unique_lock<std::mutex> sbguard(my_mutex, std::adopt_lock);

  std::adopt_lock这个参数表示标记作用,unique_lock支持更多参数,表示互斥量已经被lock了

std::adopt_lock:表示这个互斥量已经被Lock了,必须把互斥量提前lock,否则会报异常。这个标记的效果:假设调用方线程已经拥有了互斥的所有权,也就是说已经lock成功了,通知lock_guard不需要在构造函数中Lock这个互斥量了。unique_lock也是同样的功能,就是不希望在unique_lock()的构造函数中再次lock了。

std::adopt_lock

用adopt_lock的前提条件是要先给互斥量加锁,然后在后面的语句中不会再次lock(),即后续的std::lock_guard中才能使用std::adopt_lock这个参数,然后unique_lock自动unlock。

std::unique_lock<std::mutex> sbguard(my_mutex, std::adopt_lock);

  如果另外一个线程A拿了锁之后,执行20s,然后这个线程B等待A释放锁。此时,A一直占用资源,B一直得不到资源,unique_lock如果一直拿不到锁,让它去先做别的事情,所以引入了try_to_lock。

std::try_to_lock

std::try_to_lcok会尝试用 mutex的lock()去锁定mutex,但是没有锁定成功,也会立即返回,并不会阻塞。用try_to_lock的前提是不能使用lock:

std::unique_lock<std::mutex> sbguard(my_mutex, std::try_to_lock);

  a)注意,不能先去try_to_lock,尝试加锁。此时就有成功或者失败,尝试拿锁成功或者尝试拿锁失败。

  b)如果拿到了锁,才能去操作共享数据(临界区),sbguard.owns_lock拿到锁,就不能操作共享数据,可以做一些别的事情。

  c)A拿到锁,sleep 20s,此时B拿不到锁,一直在等待。线程不需要一直等资源,通过sbguard.owns_lock的值进行判断。

std::defer_lock

前提:不能自己先lock,否者会出现异常。defer_lock的意思不需要给mutex加锁:初始化了没有加锁的mutex,没加锁的mutex,可以灵活地调用unique_lock的一些重要的成员函数。

std::unique_lock<std::mutex> sbguard(my_mutex, std::defer_lock);

 之后需要手动 lock,unlock在析构函数中自动执行了。

unique_lock的成员函数

a)lock(),加锁

b)unlock(),解锁

有lock就可以有unlock,不过在unique_lock中使用了锁,类的析构函数执行了解锁的操作。有的时候会出问题,但是实际上不稳定。有的时候可能临时需要处理一些其他事情。

lock()

//处理一些共享数据

unlock()

  也可以用unqiue_lock,编码变得更加灵活:

std::unique_lock<std::mutex> sbguard(mutex);

lock();

//处理一些共享数据

unlock();

//处理一些非共享数据

lock();

////

////

  此时unlock不必手工unlock,此时的unlock在unique_lock的析构函数中执行。

try_lock()

尝试给互斥量加锁,如果拿不到锁,则返回false,如果拿到了锁,返回true,函数不阻塞。

if(sbguard.try_lock()==true)
{
//拿到锁了
访问共享数据
}else
{
//没拿到锁 }

release()

返回它所管理的mutex对象指针,并释放所有权,也就是说这个unique_lock和mutex不再有关系,严格区分unlock()和release()的区别。release()把绑在一起的东西解开了(解开关系),unlock()是解开锁头。

如果原来mutex对象处于加锁状态,需要接管过来并负责解锁。

std::unique_lock<std::mutex> sbguard(my_mutex);
std::mutex *ptx = sbguard.release();
//此时sbguard和my_mutex的关系解除了,现在就有责任自己解锁my_mutex
//如果自己不解锁,那么就卡住了

解锁语法:自己负责my_mutex的解锁。

ptx->nulock();

 release()返回的是原始的mutex指针。

锁头锁住的代码的多少成为粒度,粒度一般用粗细来描述;

a)锁住代码少,粒度细,执行效率高。

b)锁住代码多,粒度粗,执行效率低。

要学会尽量选择合适粒度的代码进行保护。

unique_lock所有权的传递

unique_lock需要和mutex绑定到一起才是一个完整的unique_lock,应该是unique_lock需要管理一个mutex指针才能起作用。

所有权:指定unique_lock拥有mutex的所有权,unique_lock可以把所有用的所有权,可以转移给其他的unique_lock对象,所有权转移,mutex可以转移,但是不能复制

可以使用std::move(mutex)

std::unique_lock<std::mutex>  sbguard1(mutex);
std::unique_lock<std::mutex> sbguard2(std::move(mutex));

 此时sbguard1为空,sbguard2接管了原来sbguard1和mutex的关系,其实就是新的unique_lock()指向了原来的mutex。

参考文献

https://blog.csdn.net/guanguanboy/article/details/100514731

C++并发与多线程学习笔记--unique_lock详解的更多相关文章

  1. C++并发与多线程学习笔记--参数传递详解

    传递临时对象 陷阱 总结 临时对象作为线程参数 线程id的概念 临时对象构造时的抓捕 成员函数指针做线程函数 传递临时对象作为线程参数 创建的工作线程不止一个,线程根据编号来确定工作内容.每个线程都需 ...

  2. 多线程05:unique_lock详解

    unique_lock详解 一.unique_lock取代lock_guard unique_lock是个类模板,实际应用中,一般lock_guard(推荐使用):lock_guard取代了mutex ...

  3. C++并发与多线程学习笔记--atomic

    std::atomic std::async std::atomic 一般atomic原子操作,针对++,--,+=,^=是支持的,其他结果可能不支持. 注意 std::atomic<int&g ...

  4. Angular6 学习笔记——组件详解之组件通讯

    angular6.x系列的学习笔记记录,仍在不断完善中,学习地址: https://www.angular.cn/guide/template-syntax http://www.ngfans.net ...

  5. Angular6 学习笔记——组件详解之模板语法

    angular6.x系列的学习笔记记录,仍在不断完善中,学习地址: https://www.angular.cn/guide/template-syntax http://www.ngfans.net ...

  6. Angular6 学习笔记——路由详解

    angular6.x系列的学习笔记记录,仍在不断完善中,学习地址: https://www.angular.cn/guide/template-syntax http://www.ngfans.net ...

  7. JavaScript学习笔记-实例详解-类(二)

    实例详解-类(二)   //===给Object.prototype添加只读\不可枚举\不可配置的属性objectId(function(){ Object.defineProperty(Object ...

  8. JavaScript学习笔记-实例详解-类(一)

    实例详解-类(一): //每个javascript函数(除了bind())都自动拥有一个prototype对象// 在未添加属性或重写prototype对象之前,它只包含唯一一个不可枚举属性const ...

  9. Android学习笔记-Dialog详解

    1.对话框的使用 1.1AlertDialog的显示 简单对话框以及监听的设置:重点掌握三个按钮(也就是三上单词): PositiveButton(确认按钮);NeutralButton(忽略按钮) ...

随机推荐

  1. ASCII Art

    ASCII Art https://npms.io/search?q=ASCII art ASCII Art Text to ASCII Art Generator (TAAG) http://pat ...

  2. Egg.js 是什么?

    Egg.js 是什么? 阿里巴巴出 Egg.js 为企业级框架和应用而生,我们希望由 Egg.js 孕育出更多上层框架,帮助开发团队和开发人员降低开发和维护成本. 注:Egg.js 缩写为 Egg 设 ...

  3. redis五种数据类型的应用

    redis的五种数据类型和使用场景 string类型 string类型多用于缓存 set key value(value可以为json字符串) setnx多用于分布式锁(后面详细整理) 计数器 inc ...

  4. Maven的-pl -am -amd参数

    本文转载自Maven的-pl -am -amd参数学习 昨天maven的deploy任务需要只选择单个模块并且把它依赖的模块一起打包,第一时间便想到了-pl参数,然后就开始处理,但是因为之前只看了一下 ...

  5. IDEA重新安装之后配置GIT

    注:此方法可用于配置gitlab也可用于配置github 1.在github中创建一个账号:https://github.com/join?source=header-home 2.下载并安装git: ...

  6. 元类、orm

    目录 一.内置函数exec 二.元类 1. 什么是元类 2. 元类的作用 3. 创建类的两种方法 4. 怎么自定义创建元类 三.ORM 1. ORM中可能会遇到的问题 2. ORM中元类需要解决的问题 ...

  7. Django框架-模型层3/数据传输/Ajax

    目录 一.orm查询优化 1.only与defer 2.select_related与prefatch_related 二.模型层choices参数 三.MTV与MVC模型 1.MVC 2.MTV 3 ...

  8. Centos7修改Docker默认存储位置

    一.前言 Centos7安装docker之后,默认的镜像及容器存储路径为/var/lib/docker,可以使用命令docker info查看. 但是该路径默认使用的是系统盘的存储,如果挂载了数据盘, ...

  9. go 动态数组 二维动态数组

    go使用动态数组还有点麻烦,比python麻烦一点,需要先定义. 动态数组申明 var dynaArr []string 动态数组添加成员 dynaArr = append(dynaArr, &quo ...

  10. 网络地址转换NAT的两种模式(概念浅析)& IP溯源

    由于全球IPv4地址越来越少.越来越贵,因此大到一个组织,小到一个家庭一个人都很难获得公网IP地址,所以只能使用内网地址,从而和别人共享一个公网IP地址.在这种情况下,NAT技术诞生. 翻译 NAT( ...