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

news2025/1/11 22:46:50

参考引用

  • 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.coloradmin.cn/o/1240725.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PDF转Word,1行Python代码就够了,免费用

大家好&#xff0c;这里是程序员晚枫。 今年十一假期没出去旅游&#xff0c;在家里更新一套原创课程&#xff0c;&#x1f449;给小白的《50讲Python自动化办公》。 所有功能&#xff0c;都只需要1行代码&#xff0c;非常适合非程序员入门Python使用。 目前全网播放量直逼100…

基于C#实现优先队列

一、堆结构 1.1性质 堆是一种很松散的序结构树&#xff0c;只保存了父节点和孩子节点的大小关系&#xff0c;并不规定左右孩子的大小&#xff0c;不像排序树那样严格&#xff0c;又因为堆是一种完全二叉树&#xff0c;设节点为 i,则 i/2 是 i 的父节点&#xff0c;2i 是 i 的…

Django报错:RuntimeError at /home/ 解决办法

错误提示&#xff1a; RuntimeError at /home/ Model class django.contrib.contenttypes.models.ContentType doesnt declare an explicit app_label and isnt in an application in INSTALLED_APPS. 原因剖析&#xff1a; 博主在使用pycharm创建Django项目的时候&#xff0…

Linux 磁盘/分区/修复 命令

目录 1. lsblk&#xff08;list block devices&#xff09; 2. fdisk&#xff08;fragment disk&#xff09; 3. gdisk 4. mkfs&#xff08;make filesystem&#xff09; 5. df&#xff08;display file-system disk space usage&#xff09; 6. du 7. fsck&#xff08;file-sy…

npm ERR!问题解决

问题一 解决办法 两个文件夹【node_global】和【node_cache】 修改文件属性 问题二 解决办法 安装淘宝镜像 npm config set registry https://registry.npm.taobao.org 查看是否成功&#xff1a; npm config get registry 是淘宝的就ok

Springboot-热部署-IDEA2023

方式一&#xff1a;jrebel 方式二&#xff1a; 1、导入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> <…

Arthas 监听 Docker 部署的java项目CPU占比高的信息

1、Linux上安装Arthas wget https://alibaba.github.io/arthas/arthas-boot.jar2、docker ps 查看目标项目的容器ID 3、copy Arthas 到目标容器中 (注意有 &#x1f615; ) docker cp arthas-boot.jar d97e8666666:/4、进入到目标容器目录中 docker exec -it d97e8666666 /b…

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

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

华为昇腾开发板共享Windows网络上网的方法

作者&#xff1a;朱金灿 来源&#xff1a;clever101的专栏 为什么大多数人学不会人工智能编程&#xff1f;>>> 具体参考文章&#xff1a;linux(内网&#xff09;通过window 上网。具体是两步&#xff1a;一是在windows上设置internet连接共享。二是打开Atlas 200I D…

5-7求三种数的和

#include<stdio.h> int main(){double sum10;double sum20;double sum30;double sum;int i;for(i1;i<100;i){sum1sum1i;}printf("sum1结果是&#xff1a;%15.6f\n",sum1);for(i1;i<50;i){sum2sum2i*i;}printf("sum2结果是&#xff1a;%15.6f\n"…

基于北方苍鹰算法优化概率神经网络PNN的分类预测 - 附代码

基于北方苍鹰算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于北方苍鹰算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于北方苍鹰优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神…

CSGO游戏搬砖市场下跌分析,是跑还是入?

CSGO市场下跌分析&#xff0c;是跑还是入&#xff1f; 以下所有都是阿阳本人最近几年观察市场和踩坑的一点经验&#xff0c;由于篇幅不长所以肯定会很浅薄&#xff0c;大伙下嘴轻点 。 首先现在真的是CSGO市场最低点吗&#xff1f;后续还会跌吗&#xff1f;我们究竟是该继续观…

【追求卓越11】算法--二叉树

引导 接下来的几节我们开始介绍非线性的数据结构--树。树的内容比较多也比较复杂。本节&#xff0c;我们只需要了解关于树的一些基本概念。以及再进一步了解树的相关内容--搜索二叉树。该类型二叉树在工作中&#xff0c;是我们常接触的。该节我们介绍关于搜索二叉树的相关操作&…

RK3588平台开发系列讲解(嵌入式AI篇)嵌入式AI模型的部署

文章目录 一、嵌入式AI模型的部署二、AI模型训练框架有哪些三、rknn-toolkit可支持转换的模型沉淀、分享、成长,让自己和他人都能有所收获!😄 📢 本篇将给大家介绍嵌入式AI模型的部署。 一、嵌入式AI模型的部署 模型的部署,是指将训练好的模型放到运行环境中进行推理的…

如何将设置为静态IP的VMware虚拟机进行克隆以便可以复刻相应的环境

一定要关闭需要克隆的虚拟机右键要选择克隆的虚拟机&#xff0c;选择管理->克隆&#xff0c;进入克隆虚拟机向导 设定克隆出来的虚拟机名称以及位置&#xff0c;选择完成 克隆完成之后将会生成虚拟机&#xff0c;示例中生成的虚拟机为ubuntu-dev2 因为原本的虚拟机为静态ip的…

Python大数据之linux学习总结——day10_hadoop原理

Hadoop原理 Hadoop基础分布式和集群Hadoop框架概述生态圈版本更新hadoop架构[重点]官方示例圆周率练习词频统计[重点]需求:步骤: Hadoop-HDFS特点hdfs架构块和副本shell命令 Hive环境准备[重点]前提启动hadoop集群启动hdfs和yarn集群启动mr历史服务检查服务 配置Hive环境变量回…

真实网络中的 bbr

本文包含中心极限定理&#xff0c;大数定律&#xff0c;经济规律等&#xff0c;bbr 倒没多少&#xff0c;不过已经习惯把 bbr 当靶子了。 上周写了 揭秘 bbr 以及 抢带宽的原理&#xff0c;我对自己说&#xff0c;这都是理论上如何&#xff0c;可实际上呢。于是有必要结合更实际…

Hadoop -hdfs的读写请求

1、HDFS写数据&#xff08;宏观&#xff09;&#xff1a; 1、首先&#xff0c;客户端发送一个写数据的请求&#xff0c;通过rpc与NN建立连接&#xff0c;NN会做一些简单的校验&#xff0c;文件是否存在&#xff0c;是否有空间存储数据等。 2、NN就会将校验的结果发送给客户端…

计算机网络之物理层(数据通信有关)

一、概述 1.1物理层引入的目的 屏蔽掉传输介质的多样性&#xff0c;导致数据传输方式的不同&#xff1b;物理层的引入使得高层看到的数据都是统一的0,1构成的比特流 1.2.物理层如何实现屏蔽 物理层靠定义的不同的通信协议&#xff08;一般称通信规程&#xff09; 这些协议…

linux服务器安装gitlab

一、安装gitlab sudo yum install curl policycoreutils-python openssh-server openssh-clients sudo systemctl enable sshd sudo systemctl start sshd sudo firewall-cmd --permanent --add-servicehttp curl https://packages.gitlab.com/install/repositories/gitla…