C++多线程编程(第二章 多线程通信和同步)

1、多线程状态

1.1线程状态说明

初始化(Init):该线程正在被创建;
就绪(Ready):该线程在就绪列表中,等待CPU调度;
运行(Running):该线程正在运行;
阻塞(Blocked):该线程被阻塞挂起,Blocked状态包括:pend(锁、事件、信号量等阻塞)、suspend(主动pend)、delay(延时阻塞)、pendtime(因为锁、事件、信号量时间等超时等待)
退出(Exit):该线程运行结束,等待父线程回收其控制块资源;
在这里插入图片描述

2、竞争状态(Race Condition)和临界区(Critical Section)

2.1、竞争状态

多线程同时读写共享数据

2.2、临界区

读写共享数据代码片段

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

3、互斥体和锁mutex

3.1互斥锁mutex

#include <thread>
#include <iostream>
#include <string>
#include <mutex>

using namespace std;
static mutex mux;//全局锁

void TestThread()
{
	//获取锁资源,如果没有锁则阻塞等待
	mux.lock();
	cout << "=======================================" << endl;
	cout << "test 001" << endl;
	cout << "test 002" << endl;
	cout << "test 003" << endl;
	cout << "=======================================" << endl;
	mux.unlock();
}

void TestThread2()
{//尝试获取锁
	//获取锁资源,如果没有锁则阻塞等待
	//mux.lock();
	for (;;)
	{
		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(3000ms);//等待一下,便于观察竞争的状态
	}
	
}
int main()
{
	printf("Demo1:\n");
	for (int i = 0; i < 2; i++)
	{
		thread th(TestThread);
		th.detach();
	}
	getchar();

	printf("Demo2:\n");
	for (int i = 0; i < 2; i++)
	{
		thread th(TestThread2);
		th.detach();
	}
	getchar();

	return 0;
}

3.2、互斥锁的坑_线程抢占不到资源的思考及解决方案

#include <thread>
#include <iostream>
#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();
		this_thread::sleep_for(1ms);//这是系统获取锁的关键,解锁后等待一下,其他线程好获取到锁,这样就可以并发的所有线程都可以在执行
	}
}


int main()
{
	//并发三个守护线程
	for (int i = 0; i < 3; i++)
	{
		thread th(ThreadMainMux,i);
		th.detach();
	}
	getchar();
	return 0;
}

解锁后没有等待的情况如下
在这里插入图片描述
解锁后等待1毫秒,让其他线程可以获取到锁,运行结果如下
在这里插入图片描述

3.3、超时锁应用 time_mutex(避免长时间死锁)

可以记录锁获取情况,多次超时,可以记录日志,获取错误情况

//超时锁应用 timed_mutex(避免长时间死锁)


#include <thread>
#include <iostream>
#include <string>
#include <mutex>

using namespace std;
//static mutex mux;//全局锁
timed_mutex tmux;//超时锁
void ThreadMainTime(int i)
{
	for (;;)
	{
		//if (!tmux.try_lock_for(chrono::milliseconds(500)))//如果超时500毫秒,返回false
		if (!tmux.try_lock_for(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()
{
	//并发三个守护线程
	for (int i = 0; i < 3; i++)
	{
		thread th(ThreadMainTime, i);
		th.detach();
	}
	getchar();
	return 0;
}

在这里插入图片描述

3.4、递归锁(可重入)recursive_mutex 和recursive_timed_mutex用于业务组合

同一个线程种同一把锁可以锁多次,避免了一些不必要的死锁;
组合业务,用到同一个锁

//可重入的锁,递归锁
#include <thread>
#include <iostream>
#include <string>
#include <mutex>

using namespace std;
//static mutex mux;//全局锁
//timed_mutex tmux;//超时锁

recursive_mutex rmux;//可重入的锁,

void Task1()
{//业务1
	rmux.lock();
	cout << "task1 [in]" << endl;
	rmux.unlock();
}
void Task2()
{//业务2
	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()
{
	//并发三个守护线程
	for (int i = 0; i < 3; i++)
	{
		thread th(ThreadMainRec, i);
		th.detach();
	}
	getchar();
	return 0;
}

在这里插入图片描述

3.5、共享锁 shared_mutex

C++ 14共享超时互斥锁 shared_timed_mutex
C++ 17共享互斥锁 shareD_mutex
查看自己vs支持哪些C++版本,如下截图
在这里插入图片描述
共享锁中包含了共享锁和互斥锁,当互斥锁被锁定,所有共享锁都进不去;共享锁中的共享锁,只要在没有锁定共享锁中的互斥锁,共享锁中的共享锁都能进入;

//共享锁,读的时候所有线程都能读,写的时候读的线程不能读,只能写

#include <thread>
#include <iostream>
#include <string>
#include <mutex>
#include <shared_mutex>

using namespace std;
//static mutex mux;//全局锁
//timed_mutex tmux;//超时锁
//recursive_mutex rmux;//可重入的锁,

//shared_mutex smux;//C++ 17支持
shared_timed_mutex stmux;//C++ 14支持



void ThreadRead(int i)
{//读的部分
	for (;;)
	{
		stmux.lock_shared();//只要没有把互斥锁锁住,共享锁都能进去
		this_thread::sleep_for(500ms);
		cout << i << "Read" << endl;
		stmux.unlock_shared();
		this_thread::sleep_for(1ms);
	}
}

void ThreadWrite(int i)
{//写的部分
	for (;;)
	{
		stmux.lock();//互斥锁锁住,共享锁都进不去
		this_thread::sleep_for(300ms);
		cout << i << "Write" << endl;
		stmux.unlock();
		this_thread::sleep_for(1ms);//防止资源无线使用
	}
}



int main()
{
	//并发三个守护线程
	for (int i = 0; i < 3; i++)
	{
		thread th(ThreadWrite, i);
		th.detach();
	}
	for (int i = 0; i < 3; i++)
	{
		thread th(ThreadRead, i);
		th.detach();
	}
	getchar();
	return 0;
}

在这里插入图片描述

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

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

相关文章

吉林大学计算机软件考研经验贴

文章目录 简介政治英语数学专业课 简介 本人23考研&#xff0c;一战上岸吉林大学软件工程专硕&#xff0c;政治72分&#xff0c;英一71分&#xff0c;数二144分&#xff0c;专业课967综合146分&#xff0c;总分433分&#xff0c;上图&#xff1a; 如果学弟学妹需要专业课资料…

Pytorch个人学习记录总结 07

目录 神经网络-非线性激活 神经网络-线形层及其他层介绍 神经网络-非线性激活 官方文档地址&#xff1a;torch.nn — PyTorch 2.0 documentation 常用的&#xff1a;Sigmoid、ReLU、LeakyReLU等。 作用&#xff1a;为模型引入非线性特征&#xff0c;这样才能在训练过程中…

leetcode 852. Peak Index in a Mountain Array(峰值索引)

一个数组保证是峰值数组&#xff08;存在一个值大于左边和右边部分数组&#xff09;&#xff0c;找出峰值的index。 要求时间复杂度在O(logn)。 思路&#xff1a; 时间复杂度为O(logn), 可以想到用binary search. 其实用O(n)的找最大值也能通过。 public int peakIndexInMou…

Spring Boot 拦截器实现:登录验证 统一异常处理 返回数据规范化

学习 Spring 和 servlet 初期&#xff0c;我们在判断用户身份时&#xff0c;都是在每个方法中获取会话、获取对象&#xff0c;这种方式冗余度高&#xff0c;增加代码复杂度&#xff0c;维护成本也高&#xff0c;因此想到可以使用 AOP 来实现一个公共的方法&#xff0c;这个公共…

android逆向环境下载记录

frida、frida_tools、obejction、wallbreaker https://github.com/frida/frida/releases pip install frida14.1.2 pip install frida-tools9.0.1 pip install objection1.9.6 https://github.com/hluwa/Wallbreaker objection -g com.hexin.plat.android explore -P ~/.objec…

JAVA基础-基于多线程的聊天程序

引言 什么是程序 &#xff1f; 一个程序可以有多个进程 。程序是一段静态的代码&#xff0c;它是应用程序执行的蓝本。 什么是进程 &#xff1f; 一个进程可以有多线程 进程是指一种正在运行的程序&#xff0c;有自己的地址空间。 作为蓝本的程序可以被多次加载到系统的不同内…

智能也是一切社会关系的总和

马克思把人作为“一切社会关系的总和”的论述中&#xff0c;他并非将自然条件作为固定的被给予的条件&#xff0c;而是作为在历史进程中&#xff0c;由于人的活动而发生的改变的被给予的条件来把握的&#xff0c;既从一开始就已经被一定的“生产关系”所塑形和中介了。智能&…

计算机启动过程uefi+gpt方式

启动过程&#xff1a; 一、通电 按下开关&#xff0c;不用多说 二、uefi阶段 通电后&#xff0c;cpu第一条指令是执行uefi固件代码。 uefi固件代码固化在主板上的rom中。 &#xff08;一&#xff09;uefi介绍 UEFI&#xff0c;全称Unified Extensible Firmware Interface&am…

Upload-Labs通关

目录 问题 我们首先先来了解一下什么是文件上传 一句话木马 web是用什么语言开发的 最简单的一句话木马 解释 了解完一句话木马 我们了解一下 蚁剑的工作原理 Pass-1 前端验证 1.通过浏览器的插件 关闭这个前端函数 2.通过bp来抓包修改后缀 Pass-2 文件类型的匹配 …

Flutter 状态组件 InheritedWidget

Flutter 状态组件 InheritedWidget 视频 前言 今天会讲下 inheritedWidget 组件&#xff0c;InheritedWidget 是 Flutter 中非常重要和强大的一种 Widget&#xff0c;它可以使 Widget 树中的祖先 Widget 共享数据给它们的后代 Widget&#xff0c;从而简化了状态管理和数据传递…

高数笔记02:导数、微分、中值定理

图源&#xff1a;文心一言 本文是我学习高等数学第二、三章导数、微分、中值定理的一些笔记和心得&#xff0c;希望可以与考研路上的小伙伴一起努力上岸~~&#x1f95d;&#x1f95d; 第1版&#xff1a;查资料、画导图、归纳题型~&#x1f9e9;&#x1f9e9; 参考用书1&…

{“msg“:“invalid token“,“code“:401}

项目场景&#xff1a; 提示&#xff1a;这里简述项目相关背景&#xff1a; {“msg“:“invalid token“,“code“:401} 前端请求 后端接口时&#xff0c; 请求失败&#xff0c;控制台出现如下所示报错信息 问题描述 问题&#xff1a; 控制台报错信息如下所示&#xff1a; …

c语言内存函数的深度解析

本章对 memcpy&#xff0c;memmove&#xff0c;memcmp 三个函数进行详解和模拟实现&#xff1b; 本章重点&#xff1a;3个常见内存函数的使用方法及注意事项并学会模拟实现&#xff1b; 如果您觉得文章不错&#xff0c;期待你的一键三连哦&#xff0c;你的鼓励是我创作的动力…

多环境配置及配置文件位置

用端口测试了一下&#xff0c;properties>yml>yaml

Java并发(十三)----共享存在的问题

1、小故事 老王&#xff08;操作系统&#xff09;有一个功能强大的算盘&#xff08;CPU&#xff09;&#xff0c;现在想把它租出去&#xff0c;赚一点外快 小南、小女&#xff08;不同的线程&#xff09;来使用这个算盘来进行一些计算&#xff0c;并按照时间给老王支付费用…

neo4j教程-安装部署

neo4j教程-安装部署 Neo4j的关键概念和特点 •Neo4j是一个开源的NoSQL图形存储数据库&#xff0c;可为应用程序提供支持ACID的后端。Neo4j的开发始于2003年&#xff0c;自2007年转变为开源图形数据库模型。程序员使用的是路由器和关系的灵活网络结构&#xff0c;而不是静态表…

【代码随想录 | Leetcode | 第十一天】字符串 | 反转字符串 | 反转字符串 II | 替换空格 | 反转字符串中的单词 | 左旋转字符串

前言 欢迎来到小K的Leetcode|代码随想录|专题化专栏&#xff0c;今天将为大家带来字符串~反转字符串 | 反转字符串 II | 替换空格 | 反转字符串中的单词 | 左旋转字符串的分享✨ 目录 前言344. 反转字符串541. 反转字符串 II剑指 Offer 05. 替换空格151. 反转字符串中的单词剑…

MATLAB与ROS联合仿真——实例程序搭建思路

一、基础运动控制实例程序搭建思路 1、需要完成的任务&#xff1a; &#xff08;1&#xff09;通过设定小车运动的速度及转角来控制ROS中小车运动。 &#xff08;2&#xff09;通过键盘输入指令控制ROS中小车运动&#xff0c;键盘输入w小车前行&#xff0c;s小车后退&#x…

Windows Server 2012 搭建网关服务器并端口转发

需求 使用 Windows server 作为Hyper-V 虚拟出许多虚拟机&#xff0c;基本上都分配了内网地址&#xff0c;现在需要这些虚拟机访问外网&#xff0c;或者外网直接访问这些虚拟机&#xff0c;必须配置一个网关服务器。我决定直接使用 Windows 的远程访问中的 NAT 服务来完成。 …

【Vue】div标签实现输入框,利用contenteditable=“true“属性的标签实现

推荐个链接&#x1f517;&#xff0c;可以更好的查阅自己遇到的问题&#xff08;点击此处即可跳转&#xff09; 使用 div 实现 input、textarea 输入框 <template><div class"content"><div class"main editTextList" ><divclass&q…