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

C++11多线程编程 二:多线程通信,同步,锁

C++11多线程编程 一:多线程概述

C++11多线程编程 二:多线程通信,同步,锁

C++11多线程编程 三:锁资源管理和条件变量 


2.1 多线程的状态及其切换流程分析

线程状态说明:

        初始化(Init):该线程正在被创建。(就是创建thread对象并设置好回调函数,该线程就处在初始化状态,初始化线程的内存空间啊等,这部分其实代码干预部分不多,也就是从初始化,到就绪态之间其实是有一个时间消耗的,这就是为什么后面使用线程池来做一个处理,来减少时间消耗,当各种内存准备好之后,就变成就绪态了)
        就绪(Ready):不代表立刻就能运行了,该线程在就绪列表中,等待 CPU 调度。
        运行(Running):该线程正在运行。被CPU调度到了。
        阻塞(Blocked):该线程被阻塞挂起。Blocked 状态包括:pend(锁、 事件、信号量等阻塞)、suspend(主动 pend)、delay(延时阻塞)、 pendtime(因为锁、事件、信号量时间等超时等待)。阻塞态就是,CPU的调度已经不再这里了,放弃CPU的调度,不浪费资源。
        退出(Exit):该线程运行结束,等待父线程回收其控制块资源。


2.2 竞争状态和临界区介绍 互斥锁mutex代码

竞争状态(Race Condition): 多线程同时读写共享数据
临界区(Critical Section): 读写共享数据的代码片段
要避免竞争状态策略, 对临界区进行保护,同时只能有一个线程进入临界区。
 
例子:期望进行整块的输出,如下,
==============================
test 001
test 002
test 003
==============================
一共创建10个线程,那么我期望每个线程输出一个这样的整块屏幕输出。写如下代码:

但是最终的结果却不是想象的那样的,而出现了乱码。

这时候需要加一个mutex的锁(mutex是一个互斥锁),需要包含头文件:#include <mutex>
static mutex mux; mux叫做互斥变量。资源被上锁后,其他线程相当于是排队等待-阻塞
mux.lock是操作系统层面的锁,mux互斥锁只有这一个,线程主动过来处理的时候必须先抢占锁资源,所以线程它是一边抢占CPU资源一边抢占锁资源。

#include <thread>
#include <iostream>
#include <string>
#include <mutex>
//Linux -lpthread
using namespace std;
static mutex mux;
void TestThread()
{for (;;){//获取锁资源,如果没有则阻塞等待//mux.lock(); //if (!mux.try_lock())  //try_lock()返回TRUE表示获得锁资源{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[])
{for (int i = 0; i < 10; i++){thread th(TestThread);th.detach();}getchar();return 0;
}

 

这样就不会出现错误了,每次都是整块的输出,而不再有乱码了。


2.3 互斥锁的坑 线程抢占不到资源原因

理想状态,当一个线程释放锁资源之后,后面的线程会排队获取锁资源,但是实际上有时会出现一个线程始终占领着这个资源,其他线程排队永远获取不到资源的情况。

你会发现结果就是一直总是这一个或者两个线程进入,而不是理想的所有的线程都能得到展示,并不是我们想要的结果,原因如下:

        这段代码中,线程1获得锁资源的时候,线程2和线程3处于阻塞态,当线程1解锁的时候,按道理2号3号线程应该有一个立即获得锁资源的,但是对于线程1来说,当它解锁的时候,又重新进入了锁,也就是解锁后自己又重新申请了锁,这个锁是操作系统内核来判断,这个锁资源有没有被占用掉,当线程1解锁后,它的内存资源当然不是立即就被释放的,因为我们的操作系统不是实时操作系统,从解锁再到锁之间可能就是微秒级别的,而CPU调度肯定是过一段时间探测一下这个资源,当线程1解锁后立即又进入了锁,操作系统来不及反应,操作系统会认为是线程1又抢到了锁资源,实际上不是,而是因为线程1的资源还没有来得及释放就又重新进入锁了,所以它没有排队就再次进入了,所以这就是坑的所在,因此在解锁和锁之前要加一个延时,给操作系统做一个释放的时间。

#include <thread>
#include <iostream>
#include <string>
#include <mutex>
//Linux -lpthread
using namespace std;
static mutex mux;void ThreadMainMux(int i)
{for (;;){mux.lock();cout << i << "[in]" << endl;this_thread::sleep_for(1000ms);mux.unlock();this_thread::sleep_for(1ms);}
}
int main(int argc, char* argv[])
{for (int i = 0; i < 3; i++){thread th(ThreadMainMux, i + 1);th.detach();}getchar();return 0;
}


2.4 超时锁timed_mutex(避免长时间死锁)和可重入锁

        mutex默认没有超时的,而有线程占用的情况下,其他线程是一直处于阻塞状态的,这种方式代码简洁,但是为后期的代码调试增加难度,比如说我们不小心在代码当中写了一个死锁,那你在调试当中你怎么去找到这个死锁呢?每次锁之前记录一下日志,看有没有进去,这样的调试成本比较大,不容易发现,只要加了timed就支持超时。

#include <thread>
#include <iostream>
#include <string>
#include <mutex>
//Linux -lpthread
using namespace std;
timed_mutex tmux;void ThreadMainTime(int i)
{for (;;){if (!tmux.try_lock_for(chrono::milliseconds(500)))  //等待时间超过500ms就超时了{cout << i << "[try_lock_for timeout]" << endl;continue;}cout << i << "[in]" << endl;this_thread::sleep_for(2000ms);  //假设要处理的业务的持续时间tmux.unlock();this_thread::sleep_for(1ms);     //防止某一个线程一直占用这个锁资源}
}int main(int argc, char* argv[])
{for (int i = 0; i < 3; i++){thread th(ThreadMainTime, i + 1);th.detach();}getchar();return 0;
}

        很多业务可能会用到同一个锁,就会出现同一个锁被锁多次的情况,如果是普通的锁的话(mutex锁),第二次锁的时候会抛出异常,若没有捕获异常,那么程序就会崩溃掉,而这个递归锁可以进行多次上锁,他会把当前线程的锁计数加一,还是处于锁的状态不会改变,有多少次lock就会有多少次unlock,直到计数变成零的时候才真正释放,可以避免不必要的死锁。 

#include <thread>
#include <iostream>
#include <string>
#include <mutex>
//Linux -lpthread
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[])
{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(默认一般值支持到C++14)
c++17 共享互斥 shared_mutex
如果只有写时需要互斥,读取时不需要,用普通的锁的话如何做
按照如下代码,读取只能有一个线程进入,在很多业务场景中,没有充分利用 cpu 资源。

        原理是,如果有一个线程在写,其他所有线程既不能读也不能写,如果说有一个线程在读,其他线程都可以读,但不能写,必须等待所有的读线程读完之后才能写,这样就确保了资源不会被多人写而出现错误。
        这就是我们要用的共享锁。读取锁一定要先释放,读取锁如果锁住的话,互斥锁也是进不去的。互斥锁一旦进入,其他的所有读取线程都在等待。其他线程的写入锁也在等待,也既同时只能有一个线程在写入。

#include <thread>
#include <iostream>
#include <string>
#include <mutex>
#include <shared_mutex>
//Linux -lpthread
using namespace std;
//c++17  共享锁
//shared_mutex smux;//c++14  共享锁 
shared_timed_mutex stmux;void ThreadRead(int i)
{for (;;){//stmux.lock_shared();共享锁,只要对方没有把互斥锁锁住,共享锁大家都可以进去。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;
}

相关文章:

C++11多线程编程 二:多线程通信,同步,锁

C11多线程编程 一&#xff1a;多线程概述 C11多线程编程 二&#xff1a;多线程通信&#xff0c;同步&#xff0c;锁 C11多线程编程 三&#xff1a;锁资源管理和条件变量 2.1 多线程的状态及其切换流程分析 线程状态说明&#xff1a; 初始化&#xff08;Init&#xff09;&am…...

js——原型和原型链

最近看了很多面试题&#xff0c;看到这个js原型是常考点&#xff0c;于是&#xff0c;我总结了一些该方面的知识点分享给大家&#xff0c;其实原型就是那么一回事&#xff0c;搞明白了就没啥了。结果如下图所示&#xff1a;原型原型又可分为显式原型和隐式原型1.1显式原型显式原…...

[计算机网络(第八版)]第三章 数据链路层(学习笔记)

物理层解决了相邻节点透明传输比特的问题 3.1 数据链路层的几个共同问题 3.1.1 数据链路和帧 链路&#xff1a; 从一个节点到相邻节点的一段物理线路&#xff0c;中间没有任何其他的交换节点 数据链路&#xff1a; 节点间的逻辑通道是把实现控制数据传输的协议的硬件和软件加…...

void在不同场景下的意义

指针一般有三种含义&#xff1a;一是指明数据的位置&#xff0c;体现在指针的值&#xff0c;表示一个地址。二是表示数据类型的大小&#xff0c;例如int指针表示四个字节为一组数据&#xff0c;体现在指针的加减法如何计算。三是表示数据如何被解释&#xff0c;例如float指针和…...

Flume简介

Flume是一个高可用&#xff0c;高可靠&#xff0c;分布式的海量日志采集、聚合和传输的系统&#xff0c;能够有效的收集、聚合、移动大量的日志数据。 优点&#xff1a; 使用Flume采集数据不需要写一行代码&#xff0c;注意是一行代码都不需要&#xff0c;只需要在配置文件中…...

java简单学习

Java 基础语法 一个 Java 程序可以认为是一系列对象的集合&#xff0c;而这些对象通过调用彼此的方法来协同工作。下面简要介绍下类、对象、方法和实例变量的概念。 对象&#xff1a;对象是类的一个实例&#xff0c;有状态和行为。例如&#xff0c;一条狗是一个对象&#xff…...

Vue2 组件基础使用、父子组件之间的传值

一、什么是组件如画红框的这些区域都是由vue里的各种组件组成、提高复用信通常一个应用会以一棵嵌套的组件树的形式来组织&#xff1a;例如&#xff0c;你可能会有页头、侧边栏、内容区等组件&#xff0c;每个组件又包含了其它的像导航链接、博文之类的组件。为了能在模板中使用…...

代码随想录算法训练营 || 贪心算法 122 55 45

Day28122.买卖股票的最佳时机II力扣题目链接给定一个数组&#xff0c;它的第 i 个元素是一支给定股票第 i 天的价格。设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易&#xff08;多次买卖一支股票&#xff09;。注意&#xff1a;你不能同时参与多笔交易…...

数据结构基础之栈和队列

目录​​​​​​​ 前言 1、栈 2、队列 2.1、实现队列 2.2、循环队列 前言 上一篇中我们介绍了数据结构基础中的《动态数组》&#xff0c;本篇我们继续来学习两种基本的数据结构——栈和队列。 1、栈 特点&#xff1a;栈也是一种线性结构&#xff0c;相比数组&#xff…...

【Spark分布式内存计算框架——Spark Streaming】3.入门案例(上)官方案例运行

2.1 官方案例运行 运行官方提供案例&#xff0c;使用【$SPARK_HOME/bin/run-example】命令运行&#xff0c;效果如下&#xff1a; 具体步骤如下&#xff1a; 第一步、准备数据源启动端口&#xff0c;准备数据 nc -lk 9999 spark spark hive hadoop spark hive 第二步、运行…...

【博学谷学习记录】超强总结,用心分享 | 架构师 Tomcat源码学习总结

文章目录TomcatTomcat功能需求分析Tomcat两个非常重要的功能&#xff08;身份&#xff09;Tomcat的架构&#xff08;设计实现&#xff09;连接器的设计连接器架构分析核心功能ProtocolHandler 组件1.EndPoint组件EndPoint类结构图2.Processor组件Processor类结构图3.Adapter组件…...

泛型<E>

泛型 案例引出泛型 按要求写出代码&#xff1a; 在ArrayList中添加3个Dog对象&#xff0c;Dog对象有name和age两个属性&#xff0c;且输出name和age public class test1 {public static void main(String[] args) {ArrayList list new ArrayList();list.add(new Dog(10,&quo…...

你对MANIFEST.MF这个文件知道多少?

前言我们在读源码过程中&#xff0c;经常看到每个jar包的METE-INF目录下有个MANIFEST.MF文件&#xff0c;这个文件到底是做什么的呢&#xff1f;在计算机领域中&#xff0c;"manifest" 通常指的是一份清单或概要文件&#xff0c;用于描述一组文件或资源的内容和属性。…...

史上最经典垃圾回收器(CMS,G1)详解、适用场景及特点、使用命令

文章目录垃圾收集器介绍总结各个垃圾收集器之间的关系垃圾收集器使用命令及默认值详解各个垃圾收集器SerialParNewParallel ScavengeSerial OldParallel OldCMS(Concurrent Mark Sweep)G1(Garbage First)适用场景及推荐垃圾收集器介绍总结 垃圾收集器可以帮助我们进行具体的垃…...

Hive查询中的优化

目录前言优化策略推荐使用group by代替distinct去重前言 优化策略 推荐使用group by代替distinct去重 参考&#xff1a; hive中groupby和distinct区别以及性能比较 - cnblogs数据倾斜之count(distinct) - cnblogs 重要结论&#xff1a; 两者都会在map阶段count&#xff0c…...

【开发规范】go项目开发中的[流程,git,代码,目录,微服务仓库管理,静态检查]

文章目录前言一、有哪些规范我们应该遵循二、项目开发流程三、git的代码分支管理1. 分支管理2. commit规范三、go的代码规范四、go项目目录规范五、微服务该采用multi-repo还是mono-repo&#xff1f;1. 引言2. Repos 是什么?3. 什么是 Mono-repo?4. Mono-repo 的劣势5. 什么是…...

数组初始化方式与decimal.InvalidOperation

数组初始化方式与decimal.InvalidOperation调用函数主函数: 数组声明不同带来的报错与否1. 报错decimal.InvalidOperation的数组初始化版本2. 可行的初始化版本输出结果1. 报错时的内容2. 正常的输出计算结果原因&#xff08;是否是数组与列表不同引起&#xff08;&#xff1f;…...

【Opencv-python】之入门安装

目录 一、安装Python 1. 登录官网https://www.python.org/downloads/ 2. 任选一个版本&#xff0c;下载Python 3. 安装Python 记得勾选下图的Add Python 3.6 PATH, 添加python到环境变量的路径&#xff0c;然后选择Install now​编辑 4. 验证是否安装成功 5.退出 二、安装…...

MySQL进阶(二)

目录 1、视图 1、检查选项 2、视图的更新 3、视图作用 2、存储过程 1、语法 2、变量 1、系统变量 2、用户定义变量 3、局部变量 3、if 4、参数 5、case 6、循环 1、while 2、repeat 3、loop 7、游标、条件处理程序 8、存储函数 3、触发器 4、锁 1、全局锁 2、表级锁 …...

热爱所有热爱

想成为这样的一个人&#xff0c;在工作中是一名充满极客精神的Programmer&#xff0c;处理遇到的问题能够游刃有余&#xff0c;能够做出优雅的设计&#xff0c;写出一手优秀的代码&#xff0c;还有着充分的学习能力和业务能力&#xff0c;做一名职场中的佼佼者。 在工作之余还能…...

Redis学习之数据删除与淘汰策略(七)

这里写目录标题一、Redis数据特征二、过期数据三、过期数据删除策略3.1 数据删除策略的目标3.2 定时删除3.3 惰性删除3.4 定期删除3.5 删除策略对比3.6 实际应用四、数据淘汰策略4.1 淘汰策略概述4.2 策略配置一、Redis数据特征 Redis是一种内存级数据库&#xff0c;所有的数据…...

HashMap 面试专题

1、HashMap 的底层结构 ①JDK1.8 以前 JDK1.8 之前 HashMap 底层是 数组和链表 结合在一起使用也就是 链表散列。HashMap 通过 key 的hashCode 函数处理过后得到 hash 值&#xff0c;然后通过 (n - 1) & hash 判断当前元素存放的位置&#xff08;这里的 n 指的是数组的长度…...

域组策略自动更新实验报告

域组策略自动更新实验报告 域组策略自动更新实验报告 作者: 高兴源 1要求、我公司为了完善员工的安全性和系统正常漏洞的维护&#xff0c;所以采用域组策略自动更新的方法来提高账户安全性&#xff0c;减少了用户的错误。 1.实验环境如下1台2008r2一台创建域&#xff0c;一台wi…...

Java自定义生成二维码(兼容你所有的需求)

1、概述作为Java开发人员&#xff0c;说到生成二维码就会想到zxing开源二维码图像处理库&#xff0c;不可否认的是zxing确实很强大&#xff0c;但是实际需求中会遇到各种各样的需求是zxing满足不了的&#xff0c;于是就有了想法自己扩展zxing满足历史遇到的各种需求&#xff0c…...

Spring事务的隔离级别

事务隔离级别解决的是多个事务同时调⽤⼀个数据库的问题 事务传播机制解决的是⼀个事务在多个节点(⽅法)中传递的问题 事务的特性: 隔离性:多个事务在并发执行的时候&#xff0c;多个事务执行的一个行为模式&#xff0c;当一个事务执行的时候&#xff0c;另一个事务执行的一个行…...

JVM系统优化实践(4):以支付系统为例

您好&#xff0c;我是湘王&#xff0c;这是我的CSDN博客&#xff0c;欢迎您来&#xff0c;欢迎您再来&#xff5e;前面说过&#xff0c;JVM会将堆内存划分为年轻代、老年代两个区域。年轻代会将创建和使用完之后马上就要回收的对象放在里面&#xff0c;而老年代则将创建之后需要…...

16- TensorFlow实现线性回归和逻辑回归 (TensorFlow系列) (深度学习)

知识要点 线性回归要点: 生成线性数据: x np.linspace(0, 10, 20) np.random.rand(20)画点图: plt.scatter(x, y)TensorFlow定义变量: w tf.Variable(np.random.randn() * 0.02)tensor 转换为 numpy数组: b.numpy()定义优化器: optimizer tf.optimizers.SGD()定义损失: …...

无自动化测试系统设计方法论

灵活 敏捷 迭代。 自动化测试 辩思 测试必不可少 想想看没有充分测试的代码, 哪一次是一次过的? 哪一次不需要经历下测试的鞭挞? 不要以为软件代码容易改, 就对于质量不切实际的自信—那是自大! 不适用自动化测试的case 遗留系统。太多的依赖方, 不想用过多的mock > …...

架构初探-学习笔记

1 什么是架构 有关软件整体结构与组件的抽象描述&#xff0c;用于指导软件系统各个方面的设计。 1.1 单机架构 所有功能都实现在一个进程里&#xff0c;并部署在一台机器上。 1.2 单体架构 分布式部署单机架构 1.3 垂直应用架构 按应用垂直切分的单体架构 1.4 SOA架构 将…...

在成都想转行IT,选择什么专业比较好?

很多创新型的互联网服务公司的核心其实都是软件&#xff0c;创新的基础、运行的支撑都是软件。例如&#xff0c;软件应用到了出租车行业&#xff0c;就形成了巅覆行业的滴滴;软件应用到了金融领域&#xff0c;就形成互联网金融;软件运用到餐饮行业&#xff0c;就形成美团;软件运…...

男女做那个的小视频网站/郴州网站seo外包

开头 此文希望能给想跳槽和面试朋友一些参考。 金九银十已过&#xff0c;面试的狂热季也已结束&#xff0c;小编也正是选择了在金九十银跳槽&#xff0c;之前在腾讯做了五年Android开发工作&#xff0c;之后感觉公司不一定能继续提供给我想要的发展空间与前景。说白了&#x…...

河北省建设厅注册中心网站首页/友链提交入口

首先参考轮子库Linux的实现&#xff0c;Linux实现的主干逻辑如下&#xff1a; Xtensa架构有其特殊性&#xff0c;最核心的一点是所谓的window ABI&#xff0c;通过window ABI减少了函数调用过程中压栈出栈的操作&#xff0c;获取了性能上的提升。不知道是不是由于 Window ABI引…...

网站的新闻模块怎么做/网站内容seo

I just dont wanna give them any more ammunition than they already have.---《老友记》 第一季 第一集 我只是不想&#xff0c;让他们有藉题发挥的机会。 名词 n. [U] 1. 弹药,军火The ammunition depot is heavily guarded. 弹药库戒备森严。 2. 【喻】"炮弹"(指…...

肃宁县网站建设/图片外链

理解的作文1000字理解在字典里是指领会&#xff0c;明白道理及事物的规律。当然我的“理解”与这字典里的理解意义相同&#xff1b;但在这里——我所要理解的却是我以前一直无法懂的那个词&#xff0c;虽然我也曾经写过它&#xff0c;夸过它&#xff1b;但写出来的全都不是肺腑…...

怎么搭建网站后台/网络营销logo

一、查询yum版本&#xff1a; yum info yum 二、查询打算下载软件的信息&#xff0c;如&#xff1a; yum search redis 或者 yum list |grep redis...

互联网公司是干啥的/seo网站的优化方案

TMemo组件属性 CaretPos 指定光标相对于编辑器的客户区域原点的X和Y的位置 当光标在TMemo组件上定位时&#xff0c;显示光标的坐标 panel1.Caption:当前位置&#xff1a;inttostr(Memo1.CaretPos.x),inttostr(Memo1.CaretPos.y); Lines 该属性以行为单位向文本编辑器添加或删除…...