【C++11并发】mutex 笔记

简介

在多线程中往往需要访问临界资源,C++11为我们提供了mutex等相关类来保护临界资源,保证某一时刻只有一个线程可以访问临界资源。主要包括各种mutex,他们的命名大都是xx_mutex。以及RAII风格的wrapper类,RAII就是一般在构造的时候上锁,在析构的时候解锁。

C++11提供的锁类型有三个:

  • mutex,头文件:
  • timed_mutex,头文件:
  • recursive_mutex,头文件:
  • recursive_timed_mutex,头文件:<shared_mutex>

C++11提供的RAII风格的wrapper类有两个:

  • lock_guard,头文件:
  • unique_lock,头文件:

std::mutex

mutex提供的方法不过,主要有lock和unlock
在这里插入图片描述
mutex只有默认构造方法,不允许拷贝构造,目前也没有提供移动构造

constexpr mutex() noexcept;
mutex( const mutex& ) = delete;

lock和try_lock的区别是,lock会阻塞当前线程,而try_lock不会,如果没有获取到锁,则返回false,如果获取到则返回true。

std::timed_mutex

相比于mutex,timed_mutex多了try_lock_for和try_lock_until两个方法。try_lock_for表示花多长时间尝试获取锁,如果超过时长则失败。try_lock_until表示尝试获取锁到什么时候,如果超过指定时间点则失败。
在这里插入图片描述

try_lock_for的函数声明如下,两个模板参数都是形参timeout_duration的,Rep表示保存时间段的类型,Period表示单位,比如秒,毫秒等。详细参考:chrono。try_lock_for表达的意思就是:阻塞获取锁,最长阻塞timeout_duration时间。如果期间获取到了锁,则返回true,否则返回false。如果timeout_duration小于等于0,try_lock_for的行为就和try_lock一样。

template< class Rep, class Period >
bool try_lock_for( const std::chrono::duration<Rep, Period>& timeout_duration );

例子:

#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>void run(std::timed_mutex &mutex)
{if (mutex.try_lock_for(std::chrono::milliseconds(500))) {std::cout << "获得了锁" << std::endl;} else {std::cout << "未获得锁" << std::endl;}
}int main() {std::timed_mutex mutex;mutex.lock();std::thread thread(run, std::ref(mutex));thread.join();mutex.unlock();return 0;
}//输出:未获得锁

try_lock_until的函数声明如下,两个模板参数都是形参timeout_time的,Clock是始终类型,Duration就是前面的std::chrono::duration。try_lock_until表达的意识就是:阻塞获取锁,一直阻塞到timeout_time这个时间点。如果期间获取到了锁,则返回true,否则返回false。time_point 参考。

template< class Clock, class Duration >
bool try_lock_until( const std::chrono::time_point<Clock, Duration>& timeout_time );

例子:

#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>void run(std::timed_mutex &mutex)
{std::chrono::time_point<std::chrono::system_clock> now = std::chrono::system_clock::now();if (mutex.try_lock_for(now + std::chrono::milliseconds(500))) {std::cout << "获得了锁" << std::endl;} else {std::cout << "未获得锁" << std::endl;}
}int main() {std::timed_mutex mutex;mutex.lock();std::thread thread(run, std::ref(mutex));thread.join();mutex.unlock();return 0;
}//输出:未获得锁

std::recursive_mutex

recursive_mutex和mutex的区别就在“recursive“,recursive_mutex允许同一个线程多次lock,当然需要相同次数的unlock。c++没有规定最多可以调用多少次,如果到达了最大lock次数,lock方法会抛出异常(std::system_error),try_lock会返回false。
在这里插入图片描述

std::recursive_timed_mutex

recursive_timed_mutex就是recursive_mutex和timed_mutex的结合体,提供的方法如下:
在这里插入图片描述

std::lock_guard

lock_guard是一个RAII风格mutex wrapper,即就是在他析构的时候,会解锁他关联的mutex。一般在构造lock_guard的时候,给mutex上锁,当然也有例外,具体得看在调用lock_guard的构造方法时传的参数。
下面是lock_guard提供的方法
在这里插入图片描述
构造方法可用的有两个

explicit lock_guard( mutex_type& m );                // 在构造lock_guard的时候调用m的lock方法,在析构的时候调用m的unlock方法
lock_guard( mutex_type& m, std::adopt_lock_t t );    // 只是关联m,但是不调用m的lock方法,在析构的时候调用m的unlock方法
lock_guard( const lock_guard& ) = delete;            // 禁止拷贝构造lock_guard

std::unique_lock

相比于lock_guard,unique_lock提供了更强大的功能,虽然不能拷贝,但是可以移动。处理支持mutex的所有操作外,还可以支持mutex延迟上锁,尝试上锁等。lock_guard提供的方法有:
在这里插入图片描述
unique_lock提供的构造方法比较多:

unique_lock() noexcept;    // 构造一个不关联mutex的unique_lock对象,他可以通过移动拷贝操作符关联到一个mutex,或者调用swap方法
unique_lock( unique_lock&& other ) noexcept;    // 移动构造
explicit unique_lock( mutex_type& m );          // 构造unique_lock的时候,调用m.lock()
unique_lock( mutex_type& m, std::defer_lock_t t ) noexcept;    // 仅关联m,但是不调用m的lock方法
// 关联m,并且调用m的try_lock方法,当然前提是m有try_lock方法,如果没有,则行为是未定义的。
// try_lock可能失败,返回false。unique_lock的构造方法没有返回值,我们怎么知道m有没有上锁成功。
// 调用unique_lock的owns_lock方法,他返回bool,可以知道m有没有上锁成功。具体可以参考例子
unique_lock( mutex_type& m, std::try_to_lock_t t );  
// 关联已经上锁的m,如果m没有上锁,则行为未定义  
unique_lock( mutex_type& m, std::adopt_lock_t t );// 构造unique_lock的时候,关联m,并且执行m.try_lock_for(timeout_duration)。
// m上锁有没有成功,仍然可以通过unique_lock的owns_lock方法获取
template< class Rep, class Period >
unique_lock( mutex_type& m, const std::chrono::duration<Rep, Period>& timeout_duration );// 和上一个方法类似,只不过执行的是m.try_lock_until(timeout_time)
template< class Clock, class Duration >
unique_lock( mutex_type& m, const std::chrono::time_point<Clock, Duration>& timeout_time );

try_to_lock的例子:

#include <iostream>
#include <mutex>std::mutex mtx;void fun() {std::unique_lock<std::mutex> guard(mtx, std::try_to_lock);if (m_guard1.owns_lock()) {std::cout << "try_to_lock success" << std::endl;} else {std::cout << "try_to_lock failed" << std::endl;}
}

当unique_lock对象成功关联到了mutex,并且他获得了锁,则在析构的时候调用mutex的unlock方法。

unique_lock仅支持移动拷贝赋值操作符

unique_lock& operator=( unique_lock&& other );

unique_lock还提供了获取mutex的方法,在调用unique_lock提供的各种lock类方法时,就如同mutex()->lock()

mutex_type* mutex() const noexcept;

unique_lock还提供了unlock方法,当调用这个方法的时候,会调用mutex的unlock方法,并且unique_lock释放mutex,即不再关联当前mutex。当时当调用unlock方法时,没有关联到mutex,或者mutex没有获得锁,则会抛出std::system_error异常。

void unlock();

unique_lock的release方法只是与关联的mutex断开关联,并不会调用mutex的unlock方法

mutex_type* release() noexcept;

bool操作符,相当于调用owns_lock(),具体使用参考如下实例:

explicit operator bool() const noexcept;
#include <iostream>
#include <mutex>
#include <thread>class Test {
public:void fun() {std::unique_lock<std::mutex> lck(m_mtx);if (bool(lck)) {std::cout << "lock succ: bool(lck)" << std::endl;}if ((bool)lck) {std::cout << "lock succ: (bool)lck" << std::endl;}if (lck) {std::cout << "lock succ: lck" << std::endl;}}private:std::mutex m_mtx;
};int main() {Test test;std::thread thread(&Test::fun, &test);thread.join();return 0;
}/** 输出结果* lock succ: bool(lck)* lock succ: (bool)lck* lock succ: lck*/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/649128.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

docker 修改默认存储位置

✨✨✨✨✨✨✨ &#x1f380;前言&#x1f381;查看前面docker储存位置&#x1f381;移动文件位置&#x1f381;修改配置文件docker.service&#x1f381;修改daemon.json&#x1f381;加载配置并重启 &#x1f380;前言 最近服务出现系统盘满了,发现其中docker存储占用很大一…

Keycloak - docker 运行 前端集成

Keycloak - docker 运行 & 前端集成 这里的记录主要是跟我们的项目相关的一些本地运行/测试&#xff0c;云端用的 keycloak 版本不一样&#xff0c;不过本地我能找到的最简单的配置是这样的 docker 配置 & 运行 keycloak keycloak 有官方(Red Hat Inc.)的镜像&#…

基于固件库的RT-THREAD移植

为什么要使用操作系统 当我们进入嵌入式这个领域的时候&#xff0c; 往往首先接触的都是单片机编程&#xff0c; 单片机编程又首选 51 单片机来入门。 这里面说的单片机编程通常都是指裸机编程&#xff0c;即不加入任何 RTOS&#xff08;Real Time Operation System 实时操作系…

药物使用不当可能会导致耳聋,尤其是这6类,需警惕

耳聋的原因有很多&#xff0c;其中之一就是药物使用不当。有些药物具有耳毒性&#xff0c;也就是说&#xff0c;它们会损害内耳的结构和功能&#xff0c;导致听力下降或丧失。这种药物性耳聋有时是可逆的&#xff0c;有时则是永久的。那么&#xff0c;到底哪些药物会导致耳聋和…

ctf-idea调试jar包

0.拿到jar包并解压 进入解压出来的目录,然后以该目录打开项目 1.设置maven 设不设置都行 2.添加依赖 添加两个依赖, boot-inf下的 classes和lib 3.配置调试器 添加 remote jvm debug 1.根据jdk版本选择调试参数 2.选择module classpath为解压后的文件夹名 如图,运行jar包的…

CDR绘图软件|安装教程来了(小白福利:有红包封面领取哦!)

前言 今天给小伙伴们讲讲&#xff1a;如何安装CDR软件。 如果未来的你想从事平面设计/广告行业&#xff0c;那应该就会接触到CDR这款软件。 CorelDRAW Graphics Suite是加拿大Corel公司的平面设计软件&#xff1b;该软件是Corel公司出品的矢量图形制作工具软件&#xff0c;这…

Confluence 的文章导入到 YouTrack KB 中

YouTrack 是有一个 KB 的&#xff0c;我们可以吧 Confluence 的文章全部导入到 YouTrack 的 KB 中。 首先&#xff0c;你需要具有管理员权限&#xff0c;然后选择导入。 然后可以在打开的界面中新增一个导入。 在新增导入中输入 Confluence 在随后的界面中输入你 Confluence …

【Hexo博客|Fluid主题】实现链接卡片效果

文章目录 前言一、CardLink库二、配置步骤1. 添加静态js文件2. 使库文件生效3. 编写启用CardLink4. 查看效果效果与前面一致。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/img_convert/06e0630f994d4d67a90e18e291c3fdc5.png#pic_center) 总结 前言 今天在阅读Github…

格子表单GRID-FORM | 嵌套子表单与自定义脚本交互

格子表单/GRID-FORM已在Github 开源&#xff0c;如能帮到您麻烦给个星&#x1f91d; GRID-FORM 系列文章 基于 VUE3 可视化低代码表单设计器嵌套表单与自定义脚本交互 新版本功能 &#x1f389; 不觉间&#xff0c;GRID-FORM 已经开源一年&#xff08;2023年1月29日首次提交…

通过FileZilla配置FTP

FileZilla服务端的安装 在虚拟机里安装FileZilla服务器 FileZilla的官网 下载一个客户端和一个服务端的FileZilla 如果已经有了一个客户端&#xff0c;可以不下用载。 FileZilla的配置 说明一下&#xff1a;通过FileZilla配置FTP有两种模式&#xff0c;我们先用被动模式 下载…

GoZero微服务个人探究之路(九)api文件编写总结

参考来源go-zero官方文档https://go-zero.dev/docs/tutorials 前言 go-zero是目前star最多的go语言微服务框架&#xff0c;api 是 go-zero特殊的语言&#xff0c;类型文件&#xff0c;go-zero自带的goctl可以通过.api文件生成http服务代码 api文件内容编写 不可使用关键字 …

Datawhale 组队学习之大模型理论基础 Task7 分布式训练

第8章 分布式训练 8.1 为什么分布式训练越来越流行 近年来&#xff0c;模型规模越来越大&#xff0c;对硬件&#xff08;算力、内存&#xff09;的发展提出要求。因为内存墙的存在&#xff0c;单一设持续提高芯片的集成越来越困难&#xff0c;难以跟上模型扩大的需求。 为了…

MATLAB|融合需求侧虚拟储能系统的楼宇微网优化调度¥29

目录 主要内容 模型研究 一、虚拟储能特征 二、楼宇微网虚拟储能 结果一览 下载链接 主要内容 该模型以楼宇为研究对象&#xff0c;围绕夏季制冷负荷&#xff0c;利用楼宇的蓄热特性&#xff0c;实现融合需求侧虚拟储能系统的楼宇微网优化调度模型&#xff0c;…

带【科技感】的Echarts 图表

Echarts脚本在线地址 https://cdn.jsdelivr.net/npm/echarts5.4.3/dist/echarts.min.js 引入Echarts 脚本后粘贴代码 vue2 代码&#xff1a; <template><div><div ref"col-2-row-2" class"col-2-row-2"></div></div> <…

力扣题目训练(1)

2024年1月25日力扣题目训练 2024年1月25日力扣题目训练225. 用队列实现栈257. 二叉树的所有路径258. 各位相加81. 搜索旋转排序数组 II82. 删除排序链表中的重复元素 II30. 串联所有单词的子串 2024年1月25日力扣题目训练 2024年1月25日开始进行编程训练&#xff0c;今天主要是…

网安渗透攻击作业(1)

实现负载均衡 第一步&#xff1a;安装依赖 sudo apt insta11 libgd-dev 第二步&#xff1a;下载nginx wget http://nginx.org/download/nginx-1.22.1.tar.gz 第三步&#xff1a;对nginx进行解压 tar -zvxf nginx-1.22.1.tar.g2 第四步&#xff1a;编译安装nginx cd ngi…

通达信动量振荡指标公式(AO),反映市场驱动力的变化

动量振荡指标AO(Awesome Oscillator)衡量的是最近5根K线的动量与过去34根K线的动量对比&#xff0c;反映市场驱动力的变化。 一、动量振荡指标公式&#xff08;副图&#xff09; 动量振荡指标AO计算公式&#xff1a; 1、计算中间价&#xff0c;将最高价和最低价的和除以2&…

浅析HTTP协议

首先&#xff0c;前端请求后端数据&#xff0c;后端响应数据给前端&#xff0c;这是我们大家都知道的&#xff0c;那其中所涉及到的数据传输协议又是什么呢&#xff1f;这个传输规范就是我们大名鼎鼎的HTTP协议&#xff01; 什么是HTTP协议&#xff1f; HTTP&#xff08;超文本…

SqlAlchemy使用教程(六) -- ORM 表间关系的定义与CRUD操作

SqlAlchemy使用教程(一) 原理与环境搭建SqlAlchemy使用教程(二) 入门示例及编程步骤SqlAlchemy使用教程(三) CoreAPI访问与操作数据库详解SqlAlchemy使用教程(四) MetaData 与 SQL Express Language 的使用SqlAlchemy使用教程(五) ORM API 编程入门 本章内容&#xff0c;稍微有…

Java链表(1)

&#x1f435;本篇文章将对单链表进行讲解&#xff0c;模拟实现单链表中常见的方法 一、什么是链表 链表是一种逻辑结构上连续而物理结构上不一定连续的线性表&#xff0c;链表由一个一个节点组成&#xff1a; 每一个节点中都由数据域&#xff08;val&#xff09;和指针域&…