当前位置: 首页 > news >正文

【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*/

相关文章:

  • 华为三层交换机之基本操作
  • leetcode88合并两个有序数组
  • python爬虫框架Scrapy
  • 案例分享 | 助力数字化转型:嘉为科技项目管理平台上线
  • CEF框架中的一些宏定义(二):CEF_CURRENTLY_ON
  • JavaEE进阶(6)SpringBoot 配置文件(作用、格式、properties配置文件说明、yml配置文件说明、验证码案例)
  • 【计算机网络】第三章·数据链路层(三)
  • 前端面试——关于this指向问题?
  • 使用Spring Boot和Tess4J实现本地与远程图片的文字识别
  • SVN 常用命令汇总(2024)
  • 上位机图像处理和嵌入式模块部署(自定义算法)
  • React16源码: React中commit阶段的commitRoot的主流程源码实现
  • 单片机学习笔记---矩阵键盘
  • 源 “MySQL 5.7 Community Server“ 的 GPG 密钥已安装,但是不适用于此软件包。请检查源的公钥 URL 是否配置正确。
  • [UI5 常用控件] 03.Icon, Avatar,Image
  • 【每日笔记】【Go学习笔记】2019-01-10 codis proxy处理流程
  • export和import的用法总结
  • HomeBrew常规使用教程
  • LeetCode算法系列_0891_子序列宽度之和
  • Linux Process Manage
  • MySQL常见的两种存储引擎:MyISAM与InnoDB的爱恨情仇
  • vue的全局变量和全局拦截请求器
  • WePY 在小程序性能调优上做出的探究
  • 基于webpack 的 vue 多页架构
  • 前端_面试
  • 【运维趟坑回忆录 开篇】初入初创, 一脸懵
  • 阿里云IoT边缘计算助力企业零改造实现远程运维 ...
  • 直播平台建设千万不要忘记流媒体服务器的存在 ...
  • ​LeetCode解法汇总2583. 二叉树中的第 K 大层和
  • #Spring-boot高级
  • #使用清华镜像源 安装/更新 指定版本tensorflow
  • %check_box% in rails :coditions={:has_many , :through}
  • (1)(1.19) TeraRanger One/EVO测距仪
  • (22)C#传智:复习,多态虚方法抽象类接口,静态类,String与StringBuilder,集合泛型List与Dictionary,文件类,结构与类的区别
  • (C语言)球球大作战
  • (附源码)springboot教学评价 毕业设计 641310
  • (机器学习-深度学习快速入门)第三章机器学习-第二节:机器学习模型之线性回归
  • (四)Tiki-taka算法(TTA)求解无人机三维路径规划研究(MATLAB)
  • (续)使用Django搭建一个完整的项目(Centos7+Nginx)
  • (转)chrome浏览器收藏夹(书签)的导出与导入
  • .NET Core 和 .NET Framework 中的 MEF2
  • .net 桌面开发 运行一阵子就自动关闭_聊城旋转门家用价格大约是多少,全自动旋转门,期待合作...
  • .netcore 获取appsettings
  • .Net调用Java编写的WebServices返回值为Null的解决方法(SoapUI工具测试有返回值)
  • .NET中 MVC 工厂模式浅析
  • .Net组件程序设计之线程、并发管理(一)
  • .sys文件乱码_python vscode输出乱码
  • .ui文件相关
  • /etc/fstab 只读无法修改的解决办法
  • @for /l %i in (1,1,10) do md %i 批处理自动建立目录
  • [ C++ ] STL---string类的使用指南
  • []我的函数库
  • [2023年]-hadoop面试真题(一)
  • [AIGC] Kong:一个强大的 API 网关和服务平台
  • [BZOJ] 2006: [NOI2010]超级钢琴