C++多线程学习(二):多线程通信和锁

参考引用

  • C++11 14 17 20 多线程从原理到线程池实战
  • 代码运行环境:Visual Studio 2019

1. 多线程状态

1.1 线程状态说明

  • 初始化 (lnit):该线程正在被创建
  • 就绪 (Ready):该线程在就绪列表中,等待 CPU 调度
  • 运行 (Running):该线程正在运行
  • 阻塞 (Blocked):该线程被阻塞挂起,Blocked 状态包括
    • pend (锁、事件、信号量等阻塞)
    • suspend (主动 pend)
    • delay (延时阻塞)
    • pendtime (因为锁、事件、信号量时间等超时)
  • 退出 (Exit):该线程运行结束,等待父线程回收其控制块资源
    • 告诉操作系统把该线程相关资源释放,不包含堆中的资源释放

在这里插入图片描述

1.2 竞争状态和临界区

  • 竞争状态 (Race Condition)
    • 多线程同时读写共享数据
  • 临界区 (Critical Section)
    • 读写共享数据的代码片段(lock 和 unlock 之间的代码)

避免竞争状态策略,对临界区进行保护,同时只能有一个线程进入临界区

2. 互斥体和锁

2.1 互斥锁

  • thread_mutex.cpp
    #include <iostream>
    #include <thread>
    #include <string>
    #include <mutex>using namespace std;static mutex mux;void TestThread() {for (;;){	// 获取锁资源,如果没有则阻塞等待(一次只能有一个线程拿到锁)// 拿锁的原则:尽晚申请、尽早释放//mux.lock();           // 拿锁方式一 if (!mux.try_lock()) {  // 拿锁方式二:可以看到多个进程在竞争拿锁的情况cout << "." << flush;this_thread::sleep_for(100ms);continue;}// 业务代码cout << "=========" << endl;cout << "Test 001" << endl;cout << "Test 002" << endl;cout << "Test 003" << endl;cout << "=========" << endl;mux.unlock();  // 如果忘记释放锁,则会导致死锁,所有线程都在等待this_thread::sleep_for(1000ms);}
    }int main(int argc, char* argv[]) {// 同时创建 10 个线程for (int i = 0; i < 10; i++) {thread th(TestThread);th.detach();}getchar();return 0;
    }
    

2.2 线程抢占不到资源

  • thread_mutex2.cpp
    #include <iostream>
    #include <thread>
    #include <string>
    #include <mutex>using namespace std;static mutex mux;void ThreadMainMux(int i) {for (;;){	mux.lock();cout << i << "[in]" << endl;this_thread::sleep_for(1000ms);mux.unlock();// 防止 unlock() 还未释放完全就进入下一个 lock(),导致其他线程拿不到锁this_thread::sleep_for(1ms);}
    }int main(int argc, char* argv[]) {// 同时创建 3 个线程for (int i = 0; i < 3; i++) {thread th(ThreadMainMux, i + 1);th.detach();}getchar();return 0;
    }
    

2.3 超时锁 timed_mutex 应用

  • 可以记录锁的获取情况,多次超时,可以记录日志,获取错误情况,避免长时间死锁
  • timed_mutex.cpp
    #include <iostream>
    #include <thread>
    #include <string>
    #include <mutex>using namespace std;timed_mutex tmux;  // 支持超时的互斥锁void ThreadMainTime(int i) {for (;;){	if (!tmux.try_lock_for(chrono::milliseconds(500))) {cout << i << "[try_lock_for timeout]" << endl;continue;}cout << i << "[in]" << endl;this_thread::sleep_for(2000ms);tmux.unlock();// 防止 unlock() 还未释放完全就进入下一个 lock(),导致其他线程拿不到锁this_thread::sleep_for(1ms);}
    }int main(int argc, char* argv[]) {getchar();// 同时创建 3 个线程for (int i = 0; i < 3; i++) {thread th(ThreadMainTime, i + 1);th.detach();}getchar();return 0;
    }
    

2.4 递归锁 recursive_mutex(可重入)

  • 同一个线程中的同一把锁可以锁多次,避免一些不必要的死锁
  • 组合业务:用到同一个锁
  • recursive_mutex.cpp
    #include <iostream>
    #include <thread>
    #include <string>
    #include <mutex>using namespace std;recursive_mutex rmux;  // 支持可重入的互斥锁void Task1() {rmux.lock();cout << "task1 [in]" << endl;rmux.unlock();
    }void Task2() {rmux.lock();cout << "task2 [in]" << endl;rmux.unlock();
    }void ThreadMainRec(int i) {for (;;){// 加锁几次对应的也要解锁几次rmux.lock();Task1();cout << i << "[in]" << endl;this_thread::sleep_for(2000ms);Task2();rmux.unlock();this_thread::sleep_for(1ms);}
    }int main(int argc, char* argv[]) {// 同时创建 3 个线程for (int i = 0; i < 3; i++) {thread th(ThreadMainRec, i + 1);th.detach();}getchar();return 0;
    }
    

2.5 共享锁 shared_mutex(解决读写问题)

  • c++14 共享超时互斥锁 shared_timed_mutex
  • 如果只有写时需要互斥,读取时不需要,用普通的锁的话如何做
  • 按照如下代码,读取只能有一个线程进入,在很多业务场景中,没有充分利用 CPU 资源

在这里插入图片描述

  • shared_mutex.cpp
    #include <iostream>
    #include <thread>
    #include <string>
    #include <mutex>
    #include <shared_mutex>using namespace std;shared_timed_mutex stmux;  // 支持可重入的共享锁 C++14// 读取线程
    void ThreadRead(int i) {for (;;){stmux.lock_shared();cout << i << " Read" << endl;this_thread::sleep_for(500ms);stmux.unlock_shared();this_thread::sleep_for(1ms);}
    }// 写入线程
    void ThreadWrite(int i) {for (;;){stmux.lock_shared();  // 只要没有锁定互斥锁,共享锁都是立即返回// 读取数据stmux.unlock_shared();// 互斥锁 写入(同时只能一个线程写入),共享锁和互斥锁都不能进入stmux.lock();  cout << i << " Write" << endl;this_thread::sleep_for(300ms);stmux.unlock();this_thread::sleep_for(1ms);}
    }int main(int argc, char* argv[]) {for (int i = 0; i < 3; i++) {thread th(ThreadWrite, i + 1);th.detach();}for (int i = 0; i < 3; i++) {thread th(ThreadRead, i + 1);th.detach();}getchar();return 0;
    }
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/202658.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux运行jmeter报错java.sql.SQLException:Cannot create PoolableConnectionFactory

在性能测试过程中遇见1个问题&#xff0c;终于解决了&#xff0c;具体问题如下。 问题 在windows电脑写jmeter脚本连接数据库连接成功 然后把该脚本放到Linux服务器上面&#xff0c;并把jmeter mysql驱动放到服务器上面&#xff0c;修改jmeter的mysql驱动路径信息 注意&…

【C++高阶(一)】二叉搜索树深度剖析

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:C从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习C   &#x1f51d;&#x1f51d; 这里写目录标题 1. 前言2. 二叉搜索树的概念以及…

【Spring源码】Spring Event事件

目录 1、前言 2、什么是Spring Event&#xff1f; 3、基本使用 3.1、定义事件 3.2、发布事件 3.3、监听事件 3.3.1、继承ApplicationListener 3.3.2、使用EventListener注解 4、Spring Event是同步还是异步&#xff1f; 4.1、源码实现 4.2、如何实现异步 4.2.1、使用…

【差分放大电路分析】2021-12-31

缘由有哪位愿意帮助一下的-嵌入式-CSDN问答 截图&#xff0c;数值自己去计算。上2图是接电阻&#xff0c;下2图是接三极管。

DNS 区域传输 (AXFR)

漏洞描述 docker环境搭建 使用 AXFR 协议的 DNS 区域传输是跨 DNS 服务器复制 DNS 记录的最简单机制。为了避免在多个 DNS 服务器上编辑信息&#xff0c;可以在一台服务器上编辑信息&#xff0c;并使用 AXFR 将信息复制到其他服务器。但是&#xff0c;如果您不保护您的服务器&…

Javascript每天一道算法题(十八)——矩阵置零-中等

文章目录 1、问题2、示例3、解决方法&#xff08;1&#xff09;方法1——标记数组 1、问题 给定一个 y x x 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 2、示例 示例 1&#xff1a; 输入&#xff1a;matrix [[…

Centos 7、Debian、Ubuntu中tree指令的检查与下载

目录 前言 Centos 7中检查tree指令是否安装的两种办法 which指令检查 查看当前版本指令 不同版本下安装tree指令 Centos 7的发行版本 重点 Debian的发行版本 重点 Ubuntu的发行版本 重点 前言 在大多数Linux发行版中&#xff0c;tree命令通常不是默认安装的指令。…

Python|合并两个字典的8种方法

在Python中&#xff0c;有多种方法可以通过使用各种函数和构造函数来合并字典。在本文中&#xff0c;我们将讨论一些合并字典的方法。 1. 使用方法update() 通过使用Python中的update()方法&#xff0c;可以将一个列表合并到另一个列表中。但是在这种情况下&#xff0c;第二个…

Linux-基本指令(1.0)

Linux是一个非常流行的操作的知识&#xff0c;并提供实例帮助读者更好地理解。让我们一起来学习吧&#xff01;系统&#xff0c;也是云计算、大数据、人工智能等领域的重要基础。学习Linux命令是Linux系统管理的基础&#xff0c;也是开发过程中必不可少的技能。本博客将介绍Lin…

springboot+vue基本微信小程序的旅游社系统

项目介绍 现今市面上有关于旅游信息管理的微信小程序还是比较少的&#xff0c;所以本课题想对如今这么多的旅游景区做一个收集和分类。这样可以给身边喜欢旅游的朋友更好地推荐分享适合去旅行的地方。 前端采用HTML架构&#xff0c;遵循HTMLss JavaScript的开发方式&#xff0…

学C的第十一天【查看汇编代码一步步了解 函数栈帧(栈区局部变量)的创建和销毁】

相关代码gitee自取&#xff1a;C语言学习日记: 加油努力 (gitee.com) 接上期&#xff1a;学C的第十天&#xff08;继续深入学习函数、函数递归、练习&#xff09;-CSDN博客 函数栈帧的创建和销毁 越高级的编译器&#xff0c;越不容易学习和观察该过程 同时在不同的编译器下&…

前缀和——238. 除自身以外数组的乘积

文章目录 &#x1f377;1. 题目&#x1f378;2. 算法原理&#x1f365;解法一&#xff1a;暴力求解&#x1f365;解法二&#xff1a;前缀和&#xff08;积&#xff09; &#x1f379;3. 代码实现 &#x1f377;1. 题目 题目链接&#xff1a;238. 除自身以外数组的乘积 - 力扣&a…

我在electron中集成了自己的ai大模型

同学们可以私信我加入学习群&#xff01; 正文开始 前言一、大模型选择二、获取key三、调用api四、调用ai模型api时&#xff0c;解决跨域总结 前言 最近单位把gpt、文心一言、通义千问、星火等等等等你能想到的ai大模型都给禁掉了&#xff0c;简直丧心病狂。 不知道有多少感同…

leetcode 343.整数拆分 198.打家劫舍(动态规划)

OJ链接 &#xff1a;leetcode 343.整数拆分 代码&#xff1a; class Solution {public int integerBreak(int n) {int[] dp new int[n1];//每个n&#xff0c;拆分多个整数乘积的最大值dp [0] 0;dp [1] 1; for(int i 2 ; i<n; i){for(int j 0 ; j < i; j){dp[i] Ma…

【JavaEE初阶】 网络编程基础与Socket套接字

文章目录 &#x1f38b;网络编程基础&#x1f6a9;为什么需要网络编程&#xff1f;&#x1f6a9;什么是网络编程&#xff1f;&#x1f6a9;网络编程中的基本概念&#x1f4cc;发送端和接收端&#x1f4cc;请求和响应&#x1f4cc;客户端和服务端&#x1f4cc;常见的客户端服务端…

03. Python中的语句

1、前言 在《Python基础数据类型》一文中&#xff0c;我们了解了Python中的基础数据类型&#xff0c;今天我们继续了解下Python中的语句和函数。 2、语句 在Python中常用的语句可以大致分为两类&#xff1a;条件语句、循环语句。 2.1、条件语句 条件语句就是我们编码时常见…

基于Haclon的Blob分析

任务要求&#xff1a; 请用BLOB分析的方法计算图中所有灰度值在120和255之间的像素构成的8连通区域的面积与中心点坐标。 Blob基础&#xff1a; 分析过程&#xff1a;首先获取图像&#xff0c;然后根据特征对原始图像进行阈值分割&#xff08;区分背景像素和前景像素&#xf…

openstack(2)

目录 块存储服务 安装并配置控制节点 安装并配置一个存储节点 验证操作 封装镜像 上传镜像 块存储服务 安装并配置控制节点 创建数据库 [rootcontroller ~]# mysql -u root -pshg12345 MariaDB [(none)]> CREATE DATABASE cinder; MariaDB [(none)]> GRANT ALL PR…

Git工作流和Commit规范

Git大家都非常熟悉了&#xff0c;就不做过多介绍&#xff0c;但是如何用好Git、如何进行合理的分支开发、Merge你是否有一个规范流程呢&#xff1f;&#x1f4a4; 不论是一个团队一起开发一个项目&#xff0c;还是自己独立开发一个项目&#xff0c;都少不了要和Git打交道&…

AI赋能数据表设计

数据表设计软件用过多种&#xff0c;用Ai 设计表几年Ai大模型爆发之后提升了新的高度 用navicat 设计表就是在跟团队的人介绍这次功能的表结构时&#xff0c;没办法看备注&#xff0c;只能看英文字段&#xff0c;导致在比较复杂的表中&#xff0c;总是在表结构和图形结构中来回…