C++线程--互斥锁的风险

前言

互斥锁的使用似乎非常简单。
只要保证代码中的关键部分,只能在任何时间点由单个线程访问就行了。一个互斥锁变量mt通过调用m.lock()和m.unlock()就保证了这种排他性。但是,魔鬼在于细节。。。


死锁

死锁的不同名字很可怕。有人称之为致命拥抱?或者死亡之吻。
但是等等,什么是死锁?
死锁是一种状态,其中至少有两个线程被阻塞,因为每个线程都在等待释放其他线程工作的某些资源,然后才释放它自己的资源。
死锁的结果是完全停顿。线程和整个程序通常被永远阻塞。死锁很容易产生,好奇吗?

// Exceptions and unknown code
std::mutex m;
m.lock();
sharedVariable= getVar();
m.unlock();

如果函数getVar()中的未知代码抛出异常,将不调用m.unlock()。
这样导致每一次请求互斥锁m的尝试都会失败,程序会永远阻塞在这里。
但这并不是这段代码唯一的问题。它调用一些未知的函数getVar(),而m.lock()则是活动的。如果函数getVar()也试图获得相同的锁,调用m.lock,你猜会发生什么?当然,你知道,死锁。


你想要一个更直观的例子吗?
按不同顺序锁定互斥锁

这里写图片描述

线程1和线程2需要访问两个资源才能完成它们的工作。不幸的是,他们要求由两个互斥体以不同的顺序保护的资源。
在这种情况下,线程执行将以这样的方式交织,即线程1得到互斥锁1,然后线程2得到互斥锁2,并且卡在这里。每个线程都希望得到另一个互斥锁。为此,线程必须等待资源的释放。


代码来描述上面的图片:

// deadlock.cpp

#include <iostream>
#include <chrono>
#include <mutex>
#include <thread>

struct CriticalData 
{
    std::mutex mut;
};

void deadLock(CriticalData& a, CriticalData& b) 
{
    a.mut.lock();
    std::cout << "get the first mutex" << std::endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(1));
    b.mut.lock();
    std::cout << "get the second mutex" << std::endl;
    // do something with a and b
    a.mut.unlock();
    b.mut.unlock();
}

int main()
{
    CriticalData c1;
    CriticalData c2;

    std::thread t1([&] 
    {
        deadLock(c1, c2); 
    });

    std::thread t2([&] 
    {
        deadLock(c2, c1); 
    });

    t1.join();
    t2.join();
}

线程t1和线程t2调用函数deadLock。
为了执行deadLock,两个函数都需要临界数据c1和c2。因为对象c1和c2必须受到共享访问的保护,所以它们有互斥锁(为了这个示例代码简短些,除了互斥锁之外没有任何其他方法或成员)。
函数中间只有一个大约1毫秒的睡眠,死锁了:

这里写图片描述

现在唯一的选择是按下Ctrl +C来杀死进程。


原文地址:

http://www.modernescpp.com/index.php/the-risk-of-mutexes
版权声明:本文为y396397735原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/y396397735/article/details/81008447

智能推荐

Thread.join()方法的使用

如果一个线程A执行了thread.join()语句,代表当前线程A等待thread线程终止后才从thread.join()方法返回 并且这个方法具有超时特性,可以添加参数设置 输出结果: jdk中Thread.join()方法的源码(进行了部门调整)   每个线程终止的条件是前驱线程的终止,每个线程等待前驱线程终止后,才从join()方法返回,  当线程终止时,会调用自身的no...

linux服务器部署jenkins笔记

安装jenkins参考文档:https://blog.csdn.net/tomatocc/article/details/83930714 1. 打开jenkins官网:https://jenkins.io/download/ 将war包下载到本地 **ps:**这里要注意的是要下载左边下方的war包,不要下载右边下面的war包。左边是稳定版本,右边是最新版本,建议大家使用稳定版本(我刚开始下载的...

k8s部署elasticsearch集群

百度营销大学     环境准备 我们使用的k8s和ceph环境见: https://blog.51cto.com/leejia/2495558 https://blog.51cto.com/leejia/2499684 ECK简介 Elastic Cloud on Kubernetes,这是一款基于 Kubernetes Operator 模式的新型编排产品,用户可使用该产品在...

saas-export项目-AdminLTE介绍与入门

AdminLTE介绍 (1)AdminLTE是什么? AdminLTE是一款建立在bootstrap和jquery之上的开源的模板主题工具 (2)AdminLTE有什么特点? 提供一系列响应的、可重复使用的组件, 并内置了多个模板页面 自适应多种屏幕分辨率,兼容PC和移动端 快速的创建一个响应式的Html5网站 AdminLTE 不但美观, 而且可以免去写很大CSS与JS的工作量 AdminLTE...

MyBatis中ResultMap结果集映射

用于解决属性名和字段名不一致的情况: resultMap 元素是 MyBatis 中最重要最强大的元素。...

猜你喜欢

编写一个shell

编写shell的过程: 1.从标准输入中读入一个字符串。 2.解析字符串 3.创建一个子进程的执行程序。 4.子进程程序替换。 5.父进程等待子进程退出。...

WEB自动化测试中Xpath定位方法

前言: Xpath是在XML文档中查找信息的一种语言,使用路径表达式来选取XML文档中的节点或节点集,由于XML与HTML结构类似(前者用于传输数据,后者用于显示数据),所以Xpath也常用于查找HTML文档中的节点或节点集。 一  路径表达式: 路径以“/”开始     表示找到满足该绝对路径的元素; 路径以//”开始  ...

力扣困难难度 第4题 寻找两个正序数组的中位数

先看一眼题 我的思路: 设置下标i,j分别用于遍历两个数组,初始值均为0,直到找到两个数组中从小到大的第第length/2个数为止结束循环,length为两个数组长度之和。 ·每次比较nums[i]nums[j],如果前者小则i++,否则j++ ·循环结束时,如果count已经达到length/2,则说明已经找到了中位数,[注意:此时有可能正好其中一个数组遍历完了!所以...

[国家集训队]小Z的袜子(莫队)

[国家集训队]小Z的袜子 题目描述 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿。终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命…… 具体来说,小Z把这NN只袜子从1到NN编号,然后从编号LL到RR(LL 尽管小Z并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,毕竟穿两只不同...

服务器配置(五) 服务器使用tomcat配置https全过程

一.了解服务器配置https协议 HTTPS,是以安全为目标的HTTP通道,简单讲是HTTP的安全版。即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。 配置HTTPS就需要证书,证书通过权威的CA机构付费获得的证书才能被互联网承认,我们将其放在服务器上面,配置好后,就可以进行https通信了。 通过https访问的网站,在地址前可以看到安全两个字,点击可以查...