std::thread线程库详解(3)
目录
前言
前两篇的博文分别介绍了标准库里面的线程和锁,这一次的博文将会介绍锁的管理。
锁在多线程编程中非常常用,但是一旦使用不谨慎就会导致很多问题,最常见的就是死锁问题。
lock_guard
std::lock_guard是最常见的管理锁的类,它会在初始化的时候自动加锁,销毁的时候自动解锁,需要锁的对象满足BasicLockable,即存在lock和unlock方法。测试代码:
void thread_func(int thread_id) {
{
std::lock_guard<std::mutex> guard(global_mutex);
std::cout << "Test 1:" << thread_id << std::endl;
std::this_thread::sleep_for(1s);
std::cout << "Test 2:" << thread_id << std::endl;
}
std::this_thread::sleep_for(0.5s);
std::cout << "Test 3:" << thread_id << std::endl;
}
std::lock_guard在线程一开始的代码块就进行了初始化,global_mutex加锁,所以Test 1和Test 2会一起输出,而之后代码块结束,std::lock_guard作为区域变量,也在此时析构释放锁。其输出为

除此之外,std::lock_guard还允许输入第二个参数std::adopt_lock_t,这个参数表明该线程已经获取了该锁,所以在创建对象的时候不需要再获取锁。
std::lock_guard<std::mutex> lk(mutex1, std::adopt_lock);
scoped_lock (C++17)
这个类和std::lock_guard类似,不过其可以同时管理多把锁,可以同时给多把锁加锁。这个方法可以很好的解决哲学家就餐问题\(^1\)。
void thread_func(int thread_id, std::mutex &mutex1, std::mutex &mutex2) {
std::scoped_lock lock(mutex1, mutex2);
std::cout << "Thread " << thread_id << " is eating." << std::endl;
std::this_thread::sleep_for(1s);
std::cout << "Thread " << thread_id << " over." << std::endl;
}
std::vector<std::shared_ptr<std::thread>> philosopher;
std::vector<std::mutex> tableware_mutex(5);
for (int loop_i = 0; loop_i < 5; ++loop_i) {
philosopher.push_back(
std::make_shared<std::thread>(thread_func, loop_i, std::ref(tableware_mutex[loop_i]), std::ref(tableware_mutex[(loop_i + 1) % 5]))
);
}
for (int loop_i = 0; loop_i < 5; ++loop_i) {
philosopher.at(loop_i)->join();
}
这里我们初始化了五个哲学家(线程)和五个餐具(锁),每个哲学家需要两个相邻的餐具来进食。其结果很简单:

可以看到,这帮哲学家们很有序的进食,没有产生冲突。而如果我们把对应的std::scoped_lock lock(mutex1, mutex2);,改为两个std::lock_guard,肉眼可见的会出现恶心的死锁问题。
和std::lock_guard类似的,它也有一个参数std::adopt_lock_t,表明线程已经获取到锁,构造时不需要获取锁,不过这个参数位于第一个。
std::scoped_lock lk(std::adopt_lock, mutex1, mutex2);
unique_lock
std::unique_lock相比较与std::lock_guard更为自由,除了std::adopt_lock_t参数外,其还支持try_to_lock_t,defer_lock_t,其中try_to_lock_t为非阻塞型加锁,defer_lock_t不在初始化的时候加锁。
std::unique_lock<std::mutex> lk(mutex, std::adopt_lock);
std::unique_lock<std::mutex> lk(mutex, std::try_to_lock);
std::unique_lock<std::mutex> lk(mutex, std::defer_lock);
std::unique_lock支持超时加锁:
std::unique_lock<std::timed_mutex> lk(mutex, 1s);
std::unique_lock支持移动语义,所以可以作为返回值
std::unique_lock<std::mutex> get_lock() {
std::unique_lock<std::mutex> lk(mutex);
return lk;
}
void thread_func(int thread_id) {
std::unique_lock<std::mutex> lk = get_lock();
std::cout << "Test 1: " << thread_id << std::endl;
std::this_thread::sleep_for(1s);
std::cout << "Test 2: " << thread_id << std::endl;
}
由于其允许在未加锁构造,所以它也提供了相应的lock、try_lock、unlock等方法。
shared_lock
和std::unique_lock类似,不过这个是锁定读写锁的读部分。
总结
本文总结了标准库中所有的锁管理的类,合理使用可以使代码更优美。这是标准库线程第三篇博文了,第四篇将会介绍线程里面的条件变量。
ref
[1] https://zh.wikipedia.org/wiki/哲学家就餐问题
博客原文:https://www.cnblogs.com/ink19/p/std_thread-3.html
std::thread线程库详解(3)的更多相关文章
- std::thread线程库详解(2)
目录 目录 简介 最基本的锁 std::mutex 使用 方法和属性 递归锁 std::recursive_mutex 共享锁 std::shared_mutex (C++17) 带超时的锁 总结 简 ...
- std::thread线程库详解(5)
目录 目录 前言 信号量 counting_semaphore latch与barrier latch barrier 总结 前言 前面四部分内容已经把目前常用的C++标准库中线程库的一些同步库介绍完 ...
- std::thread线程库详解(4)
目录 目录 前言 条件变量 一些需要注意的地方 总结 前言 本文主要介绍了多线程中的条件变量,条件变量在多线程同步中用的也比较多.我第一次接触到条件变量的时候是在完成一个多线程队列的时候.条件变量用在 ...
- Java Thread(线程)案例详解sleep和wait的区别
上次对Java Thread有了总体的概述与总结,当然大多都是理论上的,这次我将详解Thread中两个常用且容易疑惑的方法.并通过实例代码进行解疑... F区别 sleep()方法 sleep()使当 ...
- Thread线程相关方法详解
1.sleep() 使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行,但它并不释放对象锁.也就是说如果有synchronized同步快,其他线程仍然不能访问共享数据.注意该方 ...
- “全栈2019”Java多线程第十章:Thread.State线程状态详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- Lua的协程和协程库详解
我们首先介绍一下什么是协程.然后详细介绍一下coroutine库,然后介绍一下协程的简单用法,最后介绍一下协程的复杂用法. 一.协程是什么? (1)线程 首先复习一下多线程.我们都知道线程——Thre ...
- Python--urllib3库详解1
Python--urllib3库详解1 Urllib3是一个功能强大,条理清晰,用于HTTP客户端的Python库,许多Python的原生系统已经开始使用urllib3.Urllib3提供了很多pyt ...
- MySQL5.6的4个自带库详解
MySQL5.6的4个自带库详解 1.information_schema详细介绍: information_schema数据库是MySQL自带的,它提供了访问数据库元数据的方式.什么是元数据呢?元数 ...
随机推荐
- 仵航说 Vue项目如何打包并发布到linux服务器 仵老大
1,打包 1.1首先你会在本地编辑好你的代码, 1.2然后在控制台输入 npm run build npm run build 1.3稍等片刻就打包完毕 2,位置 2.1打包完毕之后会在项目中生成一个 ...
- Python-自动化测试面试
1.以你做过的项目,举例来说一下你的自动化测试是怎么做的? 参考答案:就拿简历上的ecshop项目来说吧,在编写脚本前,我们会对系统进行评估,确认这个系统可不可以实现UI自动化,如果可以的话,就筛选出 ...
- 用Python批量爬取优质ip代理
前言 有时候爬的次数太多时ip容易被禁,所以需要ip代理的帮助.今天爬的思路是:到云代理获取大量ip代理,逐个检测,将超时不可用的代理排除,留下优质的ip代理. 一.爬虫分析 首先看看今天要爬取的网址 ...
- python序列(五)切片操作
功能:截取列表中的任何部分. 切片适用于列表.元组.字符串.range对象等类型.. 格式:[::]切片使用两个冒号分隔的3个数字来完成. 第一个数字表示切片开始位置(默认为0). 第二个数字表示切片 ...
- Gitignore 配置语法
Gitignore 配置语法 原文地址:http://kuanghy.github.io/2016/05/17/gitignore Git 的 .gitignore 配置文件用于配置不需要加入到版本管 ...
- django-mdeditor支持七牛云存储图片
由于django-mdeditor官方插件没有支持第三方存储,所以,我们只能进行修改源码的方式实现了. 本次改写即使替换了其文件,不使用七牛云也是无关紧要的,因为在存储时,去settings.py中判 ...
- Android驱动学习-Eclipse安装与配置
在ubuntu系统下安装配置Eclipse软件.并且让其支持编译java程序和内核驱动程序. 1. 下载Eclipse软件. 打开官网:http://www.eclipse.org/ 点击 DOWN ...
- .NET C#中处理Url中文编码问题
近些日子在做一个用C#访问webservise的程序,由于需要传递中文参数去请求网站,所以碰到了中文编码问题.我们知道像百度这种搜索引擎中,当用户输入中文关键字后,它会把中文转码,以确保在Url中不会 ...
- 数据仓库组件:Hive环境搭建和基础用法
本文源码:GitHub || GitEE 一.Hive基础简介 1.基础描述 Hive是基于Hadoop的一个数据仓库工具,用来进行数据提取.转化.加载,是一个可以对Hadoop中的大规模存储的数据进 ...
- 第14章节 BJROBOT karto 算法构建地图【ROS全开源阿克曼转向智能网联无人驾驶车】
建地图前说明:请确保你的小车已经校正好 IMU.角速度.线速度,虚拟机配置好 ROS 网络的前提进行,否则会造成构建地图无边界.虚拟机端无法正常收到小车主控端发布的话题数据等异常情况!! 1.把小车平 ...