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

C++11并发与多线程笔记(5)互斥量概念、用法、死锁演示及解决详解

C++11并发与多线程笔记(5)互斥量概念、用法、死锁演示及解决详解

  • 1、互斥量(mutex)的基本概念
  • 2、互斥量的用法
    • 2.1 lock(),unlock()
    • 2.2 lock_guard类模板
  • 3、死锁
    • 3.1 死锁演示
    • 3.2 死锁的一般解决方案:
    • 3.3 std::lock()函数模板
    • 3.4 std::lock_guard的std::adopt_lock参数

1、互斥量(mutex)的基本概念

  • 互斥量就是个类对象,可以理解为一把,多个线程尝试用**lock()**成员函数来加锁,只有一个线程能锁定成功,如果没有锁成功,那么流程将卡在lock()这里不断尝试去锁定。
  • 互斥量使用要小心,保护数据不多也不少,少了达不到效果,多了影响效率。

2、互斥量的用法

包含#include <mutex>头文件

2.1 lock(),unlock()

步骤如下:
1. 引入头文件
2. 创建一个互斥量
3.在加锁位置:先lock() ,操作共享数据 ,unlock()

注意:lock()和unlock()要成对使用
例1:

#include<iostream>
#include<thread>
#include <vector>
#include<list>
#include<mutex>
using namespace std;class A {
public://把收到的消息(玩家命令)加入到一个队列的线程void inMsgRecvQueue() {for (int i = 0; i < 100000; i++) {cout << "inMsgRecvQueue()执行,插入一个元素 " << i << endl;//cout只输出i,不访问共享数据my_mutex.lock();msgRecvQueue.push_back(i);// 假设这个数字i就是收到的命令,直接弄到消息队列里边来my_mutex.unlock();}}bool outMsgLULProc(int& command) {my_mutex.lock();if (!msgRecvQueue.empty()) {//消息不为空command = msgRecvQueue.front();//返回第一个元素,但不检查元素是否存在msgRecvQueue.pop_front();//移除第一个元素,但不返回my_mutex.unlock();//有分支退出,就要加一个unlock与其对应return true;}my_mutex.unlock();return false;}//把数据从消息队列中取出的线程void outMsgRecvQueue() {int command = 0;for (int i = 0; i < 100000; i++) {bool result = outMsgLULProc(command);if (result == true) {cout << " outMsgRecvQueue()执行,取出一个元素" << command << endl;//可以进行命令(数据)处理}else {//消息队列为空cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl;}cout << "end" << endl;}}private:list<int> msgRecvQueue;//容器,专门用于存放玩家发来的命令mutex my_mutex;//创建一个互斥量
};int main() {A myobja;//线程通过对象的成员函数时,第二个参数需要是对象的地址,因为成员函数运行时需要用到this指针thread mythreadInMsgRecv(&A::inMsgRecvQueue, &myobja);thread mythreadOutMsgRecv(&A::outMsgRecvQueue, &myobja);mythreadInMsgRecv.join();mythreadOutMsgRecv.join();cout << "I love China!" << endl;return 0;
}

例2:

#include <iostream>
#include <thread>
#include <mutex>
#include <list>
using namespace std;list<int> test_list;
mutex myMutex;//创建互斥量void in_list(){for(int num=0;num<10000;num++){myMutex.lock();cout<<"插入数据: "<<num<<endl;test_list.push_back(num);myMutex.unlock();}}void out_list(){for(int num=0;num<10000; ++num){myMutex.lock();if(!test_list.empty()){int tmp = test_list.front();test_list.pop_front();cout<<"取出数据:"<<tmp<<endl;}myMutex.unlock();}
}
int main()
{thread in_thread(in_list);thread out_thread(out_list);in_thread.join();out_thread.join();cout << "Hello World!" << endl;return 0;
}

2.2 lock_guard类模板

  • lock_guard 对象名(myMutex);取代lock()和unlock()
  • lock_guard 构造函数执行了mutex::lock();在作用域结束时,调用析构函数,执行mutex::unlock()
  • 可以通过添加{}来减小作用域
#include <iostream>
#include <thread>
#include <mutex>
#include <list>
using namespace std;list<int> test_list;
mutex myMutex;void in_list() {for (int num = 0; num < 10000; num++) {{ //也可以多加一对括号,减小作用域lock_guard<mutex> myGuard(myMutex); //创建lock_guard对象cout << "插入数据: " << num << endl;test_list.push_back(num);}}
}void out_list() {for (int num = 0; num < 10000; ++num) {lock_guard<mutex> myGuard(myMutex);if (!test_list.empty()) {int tmp = test_list.front();test_list.pop_front();cout << "取出数据:" << tmp << endl;}}
}
int main()
{thread in_thread(in_list);thread out_thread(out_list);in_thread.join();out_thread.join();cout << "I love China!" << endl;return 0;
}

3、死锁

3.1 死锁演示

死锁至少有两个互斥量mutex1mutex2

  1. 线程A执行时,这个线程先锁 mutex1,并且锁成功了,然后去锁mutex2的时候,出现了上下文切换
  2. 上下文切换后,线程B执行,这个线程先锁mutex2,因为mutex2没有被锁,即mutex2可以被锁成功,然后线程B要去锁mutex1.
  3. 此时,死锁产生了,A锁着mutex1,需要锁mutex2,B锁着mutex2,需要锁mutex1,两个线程没办法继续运行下去。

3.2 死锁的一般解决方案:

只要保证多个互斥量上锁的顺序一样就不会造成死锁。

3.3 std::lock()函数模板

  • std::lock(mutex1,mutex2……); 一次锁定多个互斥量(一般这种情况很少),用于处理多个互斥量。
  • 如果互斥量中一个没锁住,它就等着,等所有互斥量都锁住,才能继续执行。如果有一个没锁住,就会把已经锁住的释放掉(要么互斥量都锁住,要么都没锁住,防止死锁)
  • 破坏请求与保持条件,资源一次性申请,一次性释放
#include <iostream>
#include <thread>
#include <mutex>
#include <list>
using namespace std;list<int> test_list;
mutex myMutex1;
mutex myMutex2;void in_list() {for (int num = 0; num < 10000; num++) {lock(myMutex1, myMutex2);//一次锁定多个互斥量cout << "插入数据: " << num << endl;test_list.push_back(num);myMutex2.unlock();myMutex1.unlock();}
}void out_list() {for (int num = 0; num < 10000; ++num) {lock(myMutex1, myMutex2);if (!test_list.empty()) {int tmp = test_list.front();test_list.pop_front();cout << "取出数据:" << tmp << endl;}myMutex1.unlock();myMutex2.unlock();}
}
int main()
{thread in_thread(in_list);thread out_thread(out_list);in_thread.join();out_thread.join();cout << "I love China!" << endl;return 0;
}

3.4 std::lock_guard的std::adopt_lock参数

采用std::lock函数和std::lock_guard 来联合使用:

  • my_guard(my_mutex,std::adopt_lock);
    加入adopt_lock后,在调用lock_guard的构造函数时,不再进行lock();
  • adopt_guard为结构体对象,起一个标记作用,表示这个互斥量已经lock(),不需要在lock()
#include <iostream>
#include <thread>
#include <mutex>
#include <list>
using namespace std;list<int> test_list;
mutex myMutex1;
mutex myMutex2;void in_list() {for (int num = 0; num < 10000; num++) {lock(myMutex1, myMutex2); //lock联合lock_guard使用lock_guard<mutex> mutex1(myMutex1, adopt_lock);lock_guard<mutex> mutex2(myMutex2, adopt_lock);cout << "插入数据: " << num << endl;test_list.push_back(num);}
}void out_list() {for (int num = 0; num < 10000; ++num) {lock(myMutex1, myMutex2);lock_guard<mutex> mutex1(myMutex1, adopt_lock);lock_guard<mutex> mutex2(myMutex2, adopt_lock);if (!test_list.empty()) {int tmp = test_list.front();test_list.pop_front();cout << "取出数据:" << tmp << endl;}}
}
int main()
{thread in_thread(in_list);thread out_thread(out_list);in_thread.join();out_thread.join();cout << "I love China!" << endl;return 0;
}

相关文章:

C++11并发与多线程笔记(5)互斥量概念、用法、死锁演示及解决详解

C11并发与多线程笔记&#xff08;5&#xff09;互斥量概念、用法、死锁演示及解决详解 1、互斥量&#xff08;mutex&#xff09;的基本概念2、互斥量的用法2.1 lock()&#xff0c;unlock()2.2 lock_guard类模板 3、死锁3.1 死锁演示3.2 死锁的一般解决方案&#xff1a;3.3 std:…...

华为云classroom赋能--Devstar使应用开发无需从零开始

华为云DevStar为开发者提供业界主流框架代码初始化能力&#xff0c;通过GUI、API、CLI等多种方式&#xff0c;将按模板生成框架代码的能力推送至用户桌面。同时基于华为云服务资源、成熟的DevOps开发工具链和面向多场景的众多开发模板&#xff0c;提供一站式创建代码仓、自动生…...

软件的数据回滚

原理&#xff1a;所谓的数据回滚&#xff0c;就是数据备份 增量备份&#xff1a; 全量备份&#xff1a; 最简单的事全量备份。 就是spoon工具&#xff0c;完成把所有的表每天定时复制一份&#xff0c;表名“_日期”。 所以有实时表&#xff0c;每日备份表。 回滚就是把之前…...

git clone使用https协议报错OpenSSL SSL_read: Connection was reset, errno 10054

在使用git 下载github上的代码时&#xff0c; 一般有ssh协议和https协议两种。使用ssh协议可以成功clone代码&#xff0c; 但使用https协议时出错&#xff1a; $ git clone https://github.com/openai/improved-diffusion.git Cloning into improved-diffusion... fatal: unab…...

化繁为简,使用Hibernate Validator实现参数校验

前言 在之前的悦享校园的开发中使用了SSM框架&#xff0c;由于当时并没有使用参数参数校验工具&#xff0c;方法的入参判断使用了大量的if else语句&#xff0c;代码十分臃肿&#xff0c;因此最近在重构代码时&#xff0c;将框架改为SpringBoot后&#xff0c;引入了Hibernate V…...

【Qt】多线程

线程创建 自定义线程类 #ifndef CUSTOMETHREAD_H #define CUSTOMETHREAD_H#include <QObject> #include <QThread> #include "add.h"class CustomeThread : public QThread {Q_OBJECT public:// Bind the thread kernel function.explicit CustomeThre…...

腾讯云GPU服务器GN7实例NVIDIA T4 GPU卡

腾讯云GPU服务器GN7实例搭载1颗 NVIDIA T4 GPU&#xff0c;8核32G配置&#xff0c;系统盘为100G 高性能云硬盘&#xff0c;自带5M公网带宽&#xff0c;系统镜像可选Linux和Windows&#xff0c;地域可选广州/上海/北京/新加坡/南京/重庆/成都/首尔/中国香港/德国/东京/曼谷/硅谷…...

3. 爬取自己CSDN博客列表(自动方式)(分页查询)(网站反爬虫策略,需要在代码中添加合适的请求头User-Agent,否则response返回空)

文章目录 步骤打开谷歌浏览器输入网址按F12进入调试界面点击网络&#xff0c;清除历史消息按F5刷新页面找到接口&#xff08;community/home-api/v1/get-business-list&#xff09;接口解读 撰写代码获取博客列表先明确返回信息格式json字段解读 Apipost测试接口编写python代码…...

利用HTTP代理实现请求路由

嘿&#xff0c;大家好&#xff01;作为一名专业的爬虫程序员&#xff0c;我知道构建一个高效的分布式爬虫系统是一个相当复杂的任务。在这个过程中&#xff0c;实现请求的路由是非常关键的。今天&#xff0c;我将和大家分享一些关于如何利用HTTP代理实现请求路由的实用技巧&…...

深度学习(36)—— 图神经网络GNN(1)

深度学习&#xff08;36&#xff09;—— 图神经网络GNN&#xff08;1&#xff09; 这个系列的所有代码我都会放在git上&#xff0c;欢迎造访 文章目录 深度学习&#xff08;36&#xff09;—— 图神经网络GNN&#xff08;1&#xff09;1. 基础知识2.使用场景3. 图卷积神经网…...

深入理解JVM——垃圾回收与内存分配机制详细讲解

所谓垃圾回收&#xff0c;也就是要回收已经“死了”的对象。 那我们如何判断哪些对象“存活”&#xff0c;哪些已经“死去”呢&#xff1f; 一、判断对象已死 1、引用计数算法 给对象中添加一个引用计数器&#xff0c;每当有一个地方引用它时&#xff0c;计数器就加一&…...

基于SSH框架实现的管理系统(包含java源码+数据库)

资料下载链接 介绍 基于SSH框架的管理系统 简洁版 &#xff1b; 实现 登录 、 注册 、 增 、 删 、 改 、 查 &#xff1b; 可继续完善增加前端、校验、其他功能等&#xff1b; 可作为 SSH&#xff08;Structs Spring Hibernate&#xff09;项目 开发练习基础模型&#xf…...

图像识别代做服务:实现创新应用的新契机

导言&#xff1a; 随着人工智能和图像处理技术的不断进步&#xff0c;图像识别已经成为了许多领域中的关键应用。然而&#xff0c;图像识别技术的开发和应用往往需要庞大的团队和大量的资源。这就是为什么图像识别代做服务正在崭露头角。本文将探讨图像识别代做服务如何成为实现…...

Coreutils工具包,Windows下使用Linux命令

之前总结过两篇有关【如何在Windows系统下使用Linux的常用命令】的文章&#xff1a; GnuWin32&#xff0c;Windows下使用Linux命令 UnxUtils工具包&#xff0c;Windows下使用Linux命令 今天再推荐一个类似的工具包Coreutils 一、简介 GNU core utilities是GNU操作系统基本…...

神经网络基础-神经网络补充概念-13-python中的广播

概念 在 Python 中&#xff0c;广播&#xff08;Broadcasting&#xff09;是一种用于在不同形状的数组之间执行二元操作的机制。广播允许你在不显式复制数据的情况下&#xff0c;对不同形状的数组进行运算。这在处理数组的时候非常有用&#xff0c;尤其是在科学计算、数据分析…...

HDFS原理剖析

一、概述 HDFS是Hadoop的分布式文件系统&#xff08;Hadoop Distributed File System&#xff09;&#xff0c;实现大规模数据可靠的分布式读写。HDFS针对的使用场景是数据读写具有“一次写&#xff0c;多次读”的特征&#xff0c;而数据“写”操作是顺序写&#xff0c;也就是…...

css学习2(利用id与class修改元素)

1、id选择器可以为标有特定id的html元素指定特定的样式。 2、选择器以#开头&#xff0c;后跟某id的属性值。 3、class选择器用于描述一组元素的样式&#xff0c;class可以在多个元素使用。 4、类选择器用.选择。 5、指定特定的元素使用class。 6、元素的多个类用空格分开&…...

wsl2(debian)安装python的不同版本例如3.8

要在Debian上安装 Python 3.8&#xff0c;可以按照以下步骤操作&#xff1a; 1.确保你的 Debian 系统已经更新到最新版本&#xff0c;可以使用以下命令更新&#xff1a; sudo apt update sudo apt upgrade2.安装 Python 3.8 的依赖项&#xff0c;以及构建 Python 时需要的工具…...

Python教程(9)——Python变量类型列表list的用法介绍

列表操作 创建列表访问列表更改列表元素增加列表元素修改列表元素删除列表元素 删除列表 在Python中&#xff0c;列表&#xff08;list&#xff09;是一种有序、可变的数据结构&#xff0c;用于存储多个元素。列表可以包含不同类型的元素&#xff0c;包括整数、浮点数、字符串等…...

springboot+VUE智慧公寓管理系统java web酒店民宿房屋住宿报修信息jsp源代码

本项目为前几天收费帮学妹做的一个项目&#xff0c;Java EE JSP项目&#xff0c;在工作环境中基本使用不到&#xff0c;但是很多学校把这个当作编程入门的项目来做&#xff0c;故分享出本项目供初学者参考。 一、项目描述 springbootVUE智慧公寓管理系统 系统有2权限&#xf…...

Java 语言特性(面试系列2)

一、SQL 基础 1. 复杂查询 &#xff08;1&#xff09;连接查询&#xff08;JOIN&#xff09; 内连接&#xff08;INNER JOIN&#xff09;&#xff1a;返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...

《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)

CSI-2 协议详细解析 (一&#xff09; 1. CSI-2层定义&#xff08;CSI-2 Layer Definitions&#xff09; 分层结构 &#xff1a;CSI-2协议分为6层&#xff1a; 物理层&#xff08;PHY Layer&#xff09; &#xff1a; 定义电气特性、时钟机制和传输介质&#xff08;导线&#…...

深入理解JavaScript设计模式之单例模式

目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式&#xff08;Singleton Pattern&#…...

【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统

目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索&#xff08;基于物理空间 广播范围&#xff09;2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

站群服务器的应用场景都有哪些?

站群服务器主要是为了多个网站的托管和管理所设计的&#xff0c;可以通过集中管理和高效资源的分配&#xff0c;来支持多个独立的网站同时运行&#xff0c;让每一个网站都可以分配到独立的IP地址&#xff0c;避免出现IP关联的风险&#xff0c;用户还可以通过控制面板进行管理功…...

为什么要创建 Vue 实例

核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...

数学建模-滑翔伞伞翼面积的设计,运动状态计算和优化 !

我们考虑滑翔伞的伞翼面积设计问题以及运动状态描述。滑翔伞的性能主要取决于伞翼面积、气动特性以及飞行员的重量。我们的目标是建立数学模型来描述滑翔伞的运动状态,并优化伞翼面积的设计。 一、问题分析 滑翔伞在飞行过程中受到重力、升力和阻力的作用。升力和阻力与伞翼面…...

TCP/IP 网络编程 | 服务端 客户端的封装

设计模式 文章目录 设计模式一、socket.h 接口&#xff08;interface&#xff09;二、socket.cpp 实现&#xff08;implementation&#xff09;三、server.cpp 使用封装&#xff08;main 函数&#xff09;四、client.cpp 使用封装&#xff08;main 函数&#xff09;五、退出方法…...

JS红宝书笔记 - 3.3 变量

要定义变量&#xff0c;可以使用var操作符&#xff0c;后跟变量名 ES实现变量初始化&#xff0c;因此可以同时定义变量并设置它的值 使用var操作符定义的变量会成为包含它的函数的局部变量。 在函数内定义变量时省略var操作符&#xff0c;可以创建一个全局变量 如果需要定义…...

2025年低延迟业务DDoS防护全攻略:高可用架构与实战方案

一、延迟敏感行业面临的DDoS攻击新挑战 2025年&#xff0c;金融交易、实时竞技游戏、工业物联网等低延迟业务成为DDoS攻击的首要目标。攻击呈现三大特征&#xff1a; AI驱动的自适应攻击&#xff1a;攻击流量模拟真实用户行为&#xff0c;差异率低至0.5%&#xff0c;传统规则引…...