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;
    }
    

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

实验7设计建模工具的使用(三)

二&#xff0c;实验内容与步骤 1. 百度搜索1-2张状态图&#xff0c;请重新绘制它们&#xff0c;并回答以下问题&#xff1a; 1&#xff09;有哪些状态&#xff1b; 2&#xff09;简要描述该图所表达的含义&#xff1b; 要求&#xff1a;所绘制的图不得与本文中其它习题一样…

电磁优化的并行空间映射方法

空间映射(SM)是一种公认的加速电磁优化的方法。现有的SM方法大多基于顺序计算机制。本文提出了一种用于电磁优化的并行SM方法。在该方法中&#xff0c;每次迭代开发的代理模型被训练以同时匹配多个点的精细模型。多点训练和SM使代理模型在比标准SM更大的邻域内有效。本文提出的…

五大资源之Service(可以固定IP)

Service可以看作是一组同类Pod对外访问接口,借助Service应用可以方便的实现服务发现与负载均衡 创建集群内部可以访问Service #暴露Service(也创建在了namespace dev下) [root@master ~]# kubectl expose deployment(pod控制器) nginx --name=svc-nginx1 --type=Cluste…

MySQL数据库入门到大牛_基础_12_MySQL数据类型精讲

文章目录 1. MySQL中的数据类型2. 整数类型2.1 类型介绍2.2 可选属性2.2.1 M2.2.2 UNSIGNED2.2.3 ZEROFILL 2.3 适用场景2.4 如何选择&#xff1f; 3. 浮点类型3.1 类型介绍3.2 数据精度说明3.3 精度误差说明 4. 定点数类型4.1 类型介绍4.2 开发中经验 5. 位类型&#xff1a;BI…

PyTorch 之 Dataset 类入门学习

PyTorch 之 Dataset 类入门学习 Dataset 类简介 PyTorch 中的 Dataset 类是一个抽象类&#xff0c;用来表示数据集。通过继承 Dataset 类可以进行自定义数据集的格式、大小和其它属性&#xff0c;供后续使用&#xff1b; 可以看到官方封装好的数据集也是直接或间接的继承自 …

《微信小程序案例大全》大学生期末大作业可以直接使用!!

前言 在大学生活中&#xff0c;期末大作业是锻炼和展示自己所学知识的重要时刻。微信小程序作为一种快速、便捷的应用开发方式&#xff0c;成为了大学生开发实践的热门选择。本文将为大家推荐一系列可以直接使用的微信小程序案例&#xff0c;包括仿真社交、图书管理、学习工具…

智慧城市内涝积水监测仪功能,提升城市预防功能

内涝积水监测仪不仅改变了人们应对城市内涝的老办法&#xff0c;还让智慧城市往前迈了一大步。这个监测仪是怎么做到的呢&#xff1f;就是靠它精准的数据监测和预警&#xff0c;让城市管理有了更科学高效的解决妙招。它就像有了个聪明又负责任的助手&#xff0c;让城市管理更加…

排序算法-----快速排序(非递归实现)

目录 前言 快速排序 基本思路 非递归代码实现 前言 很久没跟新数据结构与算法这一栏了&#xff0c;因为数据结构与算法基本上都发布完了&#xff0c;哈哈&#xff0c;那今天我就把前面排序算法那一块的快速排序完善一下&#xff0c;前面只发布了快速排序递归算法&#xff0c;…

不到十个例题带你拿下c++双指针算法(leetcode)

移动零问题 https://leetcode.cn/problems/move-zeroes/submissions/ 1.题目解析 必须在原数组进行修改&#xff0c;不可以新建一个数组 非零元素相对顺序不变 2.算法原理 【数组划分】【数组分块】 这一类题会给我们一个数组&#xff0c;让我们划分区间&#xff0c;比如…

C++虚析构和纯虚析构解决delete堆区父类指针无法调用子类的构造函数

#include<iostream> #include<string>using namespace std;//虚析构和纯虚析构 class Animal { public:Animal(){cout<<"执行Animal的构造函数"<<endl;}~Animal(){cout<<"执行Animal的析构函数"<<endl;}virtual void …

对接苹果支付退款退单接口

前言 一般而言&#xff0c;我们其实很少对接退款接口&#xff0c;因为退款基本都是商家自己决定后进行操作的&#xff0c;但是苹果比较特殊&#xff0c;用户可以直接向苹果发起退款请求&#xff0c;苹果觉得合理会退给用户&#xff0c;但是目前公司业务还是需要对接这个接口&am…

蓝桥杯每日一题2023.11.22

题目描述 题目分析 由题目知其每个品牌积分一定小于315故直接暴力枚举每个品牌如果符合要求直接输出即可 &#xff08;答案&#xff1a;150&#xff09; #include<bits/stdc.h> using namespace std; int main() {for(int i 1; i < 315; i ){for(int j 1; j <…

【无标题】dp80采集机和机器人通信相关框架总结

采血机器人通信解析相关框架总结: 类似于dp80,将整个过程进行了分解如下: 类似于dp80,将整个过程进行了分解如下: 上位机界面在进行点击操作的时候,先是通信协议的解析,解析后改变采血的控制状态如下: Dp80主要框架解析࿱

层次分析法--可以帮助你做决策的简单算法

作用 层次分析法是一个多指标的评价算法&#xff0c;主要用来在做决策时&#xff0c;给目标的多个影响因子做权重评分。特别是那些需要主观决策的、或者需要用经验判断的决策方案&#xff0c;例如&#xff1a; 买房子&#xff08;主观决策&#xff09;选择旅游地&#xff08;…

RabbitMQ快速入门(简单收发消息)

文章目录 前言一、数据隔离1.用户管理2.virtual host 二、控制台收发1.交换机2.队列3.绑定 三、编程式收发1.依赖和配置2.收发信息 总结 前言 1.了解数据隔离 2.RabbitMQ控制台收发信息 3.SpringBoot整合RabbitMQ收发信息 一、数据隔离 1.用户管理 点击Admin选项卡&#xff0…

zookeeper单机版的搭建

一 zookeeper的搭建 1.1 上传zkjar包 1.2 搭建配置 1.解压压缩包 [rootlocalhost export]# tar -zxvf zookeeper-3.7.0-bin.tar.gz 2.创建data文件夹 [rootlocalhost export]# cd apache-zookeeper-3.7.0-bin/ [rootlocalhost apache-zookeeper-3.7.0-bin]# ls bin conf…

Java进阶——多线程相关,实际应用中的积累,持续更新

目录 多线程相关CountDownLatch赛跑的案例countDownLatch.await(300, TimeUnit.SECONDS); Java其他进阶Map的put方法只放一个元素的集合 多线程相关 CountDownLatch 案例&#xff1a;主线程的执行需要等待子线程执行完&#xff0c;等各个线程执行完毕后&#xff0c;主线程做收…

使用gin 代理 web网页

问web项目的代理&#xff0c;业界常用的方案是nginx做代理&#xff0c;这个是网上最多资料的。 因为我需要做自己的流量转发&#xff0c;也就是所有访问都要经过我的一个流量分发微服务&#xff0c;这和nginx作用冲突了。如果再加个nginx来做第一层方向代理和网页的静态资源代…

Linux学习第45天:Linux 多点电容触摸屏实验(三):难忘记第一次牵你手的温存

Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长 本章的思维导图如下&#xff1a; 五、tslib移植与使用 通过 tslib 来直观的测试多点电容触摸屏驱动。 1、tslib移植 1&#xff09;、获取tslib源码 git 地址为…

语音识别技术在医疗行业中的应用案例

随着语音识别技术和计算机视觉技术的不断提高&#xff0c;现代医学正在进入全面数字化时代。 追求高质量的训练数据是人工智能产业的信条&#xff0c;得到更为精准的语音机器模型更离不开语音数据的不断供给。本文讲介绍: 什么是语音识别技术语音识别技术如何应用于医疗行业 …