C++并发与多线程学习笔记--互斥量、用法、死锁概念
- 互斥量(mutex)的基本概念
- 互斥量的用法
- lock(), unlock()
- std::lock_guard类模板
- 死锁
- 死锁演示
- 死锁的一般解决方案
- std::lock()函数模板
- std::lock_guard的std::adopt_lock参数
互斥量(mutex)的基本概念
线程保护共享数据,操作时,某个线程用代码把共享数据锁住,其他想操作共享数据的线程必须等待;等待解锁,其他线程才能继续操作共享数据。
1)锁定住
2)操作
3)解锁
“互斥量”的基本概念,互斥量是个类对象,理解成一把锁,多个线程尝试用lock()成员函数来加锁这个锁头,只有一个线程能锁定成功,只要返回了表示锁成功了,如果没锁成功,那么流程就会卡在Lock()这里不断尝试去锁,如果永远不成功就“死锁”。
互斥量的用法
首先,使用互斥量需要引入一个头
#include <mutex>
lock(), unlock()
线程先lock(),操作共享数据,然后操作完之后,unlock();
lock()和unlock()要成对使用,有lock()必然要有unlock,每调用一次lock(),必然应该调用一次unlock()。不应该出现一次lock(),两次unlock(),非对称的数据调用必然会出错!!!lock和unlock用来保护代码,由程序开发者决定,明确决定从哪里保护到哪。
class ProcessRequest {
public:
//把命令加入到一个队列
void inMsgRecvQueue() {
for (int i = 0; i < 100000; ++i) {
my_mutex.lock();
cout << "插入一个元素" << endl;
m_msgRecvQueue.push_back(i); //假设这个队列表示玩家的命令
my_mutex.unlock();
} //占用时间片
}
bool outMsgLULProc(int &command) {
if (!m_msgRecvQueue.empty()) {
int command = m_msgRecvQueue.front();
m_msgRecvQueue.pop_front();
return true;
}
return false;
}
//把命令移出一个队列
void outMsgRecvQueue() {
int command = 0;
for (int i = 0; i < 100000; ++i) {
my_mutex.lock();
bool result = outMsgLULProc(command);
if (result == true) {
cout << "outMsgRecvQueue() 执行,取出一个元素" << endl;
}
else
{
cout << "outMsgRecvQueue() 还执行,但是消息队列为空" << endl;
//消息队列为空
}
my_mutex.unlock();
//占用时间片
}
}
private:
std::list<int> m_msgRecvQueue; //容器,用于表示玩家的发送过来命令
std::mutex my_mutex;
};
std::lock_guard类模板
为了防止忘记写unlock,std::lock_guard,可以自动unlock,功能类似unique_ptr。类模板可以直接取代lock和unlock,同时取代两个,也就是说用了lock_guard之后不能使用lock和unlock了。
lock_guard构造函数里执行了 mutex::lock(),在析构函数里执行了mutex::unlock()。超出作用域析构,不用担心结果。
死锁
在操作系统中,进程互相竞争资源,但是资源有限,都在等待占用资源,运行完毕释放资源。C++中,至少两个锁才会产生死锁问题,一把锁不会产生死锁。多线程以及多进程改善了系统资源的利用率并提高了系统 的处理能力。然而,并发执行也带来了新的问题——死锁。
死锁是指两个或两个以上的进程(线程)在运行过程中因争夺资源而造成的一种僵局(Deadly-Embrace) ) ,若无外力作用,这些进程(线程)都将无法向前推进。
某计算机系统中只有一台打印机和一台输入设备,进程P1正占用输入设备,同时又提出使用打印机的请求,但此时打印机正被进程P2 所占用,而P2在未释放打印机之前,又提出请求使用正被P1占用着的输入设备。这样两个进程相互无休止地等待下去,均无法继续执行,此时两个进程陷入死锁状态。
关于死锁的一些结论:
a)参与死锁的进程数至少为两个
b)参与死锁的所有进程均等待资源
c)参与死锁的进程至少有两个已经占有资源
d)死锁进程是系统中当前进程集合的一个子集
e)死锁会浪费大量系统资源,甚至导致系统崩溃
死锁
两个线程A和B:
a)A执行的时候,先锁金锁,金锁lock()成功,程序往下走,然后lock()银锁,还没锁成功,此时因为线程调度,上下文切换,一不小心,线程A切换走了。
b)线程B得到了执行的权限,先锁银锁,然后线程B要去lock金锁,此时此刻,死锁产生了。
分析:线程A,拿着金锁,等银锁。线程B,拿着银锁,等金锁。此时,两个线程流程都走不下去,所以后面的代码有解锁金锁的解不开,线程B拿不到金锁,流程走不下去,银锁解不开。
死锁的一般解决方案
只要调用顺序保持一致,就不会死锁。
Processor1{
mutex1.lock();
mutex2.lock();
//
....
//
mutex1.unlock();
mutex2.unlock();
}
Processor2{
mutex1.lock();
mutex2.lock();
//
...
//
mutex1.unlock();
mutex2.unlock();
}
std::lock()函数模板
std::lock_guard的std::adopt_lock参数
std::adopt_lock是个结构体对象,起一个标记的作用:这个互斥量已经lock(),不需要std::lock_guard<std:mutex> 构造函数里面再面对Mutex对象进行lock()。
参考文献
https://blog.csdn.net/wljliujuan/article/details/79614019
C++并发与多线程学习笔记--互斥量、用法、死锁概念的更多相关文章
- C++并发与多线程学习笔记--future成员函数、shared_future、atomic
std::future的其他成员函数 std::shared_future 原子操作.概念.基本用法 多线程主要是为了执行某个函数,本文的函数的例子,采用如下写法 int mythread() { c ...
- C++并发与多线程学习笔记--单例设计模式、共享数据分析
设计模式 共享数据分析 call_once 设计模式 开发程序中的一些特殊写法,这些写法和常规写法不一样,但是程序灵活,维护起来方便,别人接管起来,阅读代码的时候都会很痛苦.用设计模式理念写出来的代码 ...
- C++并发与多线程学习笔记--多线程数据共享问题
创建和等待多个线程 数据和共享问题分析 只读的数据 有读有写 其他案例 共享数据的保护案例代码 创建和等待多个线程 服务端后台开发就需要多个线程执行不同的任务.不同的线程执行不同任务,并返回执行结果. ...
- C++并发与多线程学习笔记--线程之间调度
condition_variable wait() notify_one notify_all condition_variable 条件变量的实际用途: 比如有两个线程A和B,在线程A中等待一个条件 ...
- C++并发与多线程学习笔记--unique_lock详解
unique_lock 取代lock_quard unique_lock 的第二个参数 std::adopt_lock std::try_to_lock std::defer_lock unique_ ...
- C++并发与多线程学习笔记--基本概念和实现
基本概念 并发 可执行程序.进程.线程 学习心得 并发的实现方法 多进程并发 多线程并发 总结 C++标准库 基本概念 (并发.进程.线程)区分C++初级编程和中高级编程 并发 两个或者更多的任务同时 ...
- C++并发与多线程学习笔记--atomic
std::atomic std::async std::atomic 一般atomic原子操作,针对++,--,+=,^=是支持的,其他结果可能不支持. 注意 std::atomic<int&g ...
- C++并发与多线程学习笔记--参数传递详解
传递临时对象 陷阱 总结 临时对象作为线程参数 线程id的概念 临时对象构造时的抓捕 成员函数指针做线程函数 传递临时对象作为线程参数 创建的工作线程不止一个,线程根据编号来确定工作内容.每个线程都需 ...
- C++并发与多线程学习笔记--async、future、packaged_task、promise
async future packaged_task promise async std:async 是个函数,用来启动一个异步任务,启动起来一个异步任务之后,返回一个std::futre对象,启动一 ...
随机推荐
- 百万SPC即将空投,3.0公链NGK有多“豪横”?
在1月2日晚间,比特币强势突破3万美金,随后还在一路上涨,现在价格33431.64美金.仅用了不到一个月的时间,比特币就从2万美金涨到了3万美金,这充分展示了市场对于数字货币的强烈信心.没有了天花板的 ...
- NGK公链账本技术浅析
NGK公链账本是一个去中心化加密账本,运行在分布式网络上.分布式账本是区块链技术中最重要的组成部分之一.NGK作为公链资产,在公链中起到桥梁作用,可以促进其他资产(法币.数字资产.股权以及实物资产)交 ...
- Go之Casbin简介,安装,模型,存储,函数
简介 Casbin是一个强大的,高效的开源访问控制框架,其权限管理机制支持多种访问控制模型 支持编程语言 不同语言中支持的特性 我们一直致力于让 Casbin 在不同的编程语言中拥有相同的特性. 但是 ...
- now-go时间百宝箱
golang不像C#,Java这种高级语言,有丰富的语法糖供开发者很方便的调用.所以这便催生出很多的开源组件,通过使用这些第三方组件能够帮助我们在开发过程中少踩很多的坑. 时间处理是所有语言都要面对的 ...
- Nifi组件脚本开发—ExecuteScript 使用指南(三)
上一篇:Nifi组件脚本开发-ExecuteScript 使用指南(二) Part 3 - 高级特征 本系列的前两篇文章涵盖了 flow file 的基本操作, 如读写属性和内容, 以及使用" ...
- tomcat运行多个项目同一个端口与不同端口的设置
一.首先打包项目 这里采用eclipse开发工具,选中项目右击,点击Export进入 选择web下的 WAR file ,点击next 在这里可能有坑,新装的eclipse没有web文件夹 此时需要下 ...
- idea将文件push之后如何回退
- SpringCloud之服务配置
1.config 1.1定义 对于分布式微服务,有很多的配置,那么修改起来很麻烦.这就需要对这些配置文件进行集中式的管理,config的功能就是用来统一管理配置文件的.它为微服务提供集中化的外部配置支 ...
- 异常控制流(csapp)
[前言]程序按照一定顺序执行称为控制转移.最简单的是平滑流,跳转.调用和返回等指令会造成平滑流的突变.系统也需要能够对系统状态的变化做出反应,这些系统状态不能被内部程序变量捕获但是,操作系统通过使控制 ...
- Linear Algebra From Data
Linear Algebra Learning From Data 1.1 Multiplication Ax Using Columns of A 有关于矩阵乘法的理解深入 矩阵乘法理解为左侧有是一 ...