C++多线程学习[四]:多线程的通信和同步、互斥锁、超时锁、共享锁

一、多线程的状态

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

在这里插入图片描述

二、多线程之间的竞争关系和临界区

1、竞争关系(Race condition)

多个线程同时读写共享资源
例如:

#include<iostream>
#include<thread>
#include<string>
using namespace std;
void Test()
{
	cout << "---------------" << endl;
	cout << "Test 1 " << endl;
	cout << "Test 2 " << endl;
	cout << "-----------------" << endl;
}

int main()
{
	for (int i = 0; i < 10; i++)
	{
		thread th(Test);
		th.detach();
	}
	getchar();
	return 0;
}

在这里插入图片描述

我们创建了10个线程,但是每个线程在运行时都是无规律的,对于相同的输出都是处于竞争状态。

2、临界区(Critical Section)

我们使用C++ 11 自带的mutex(互斥锁)来解决竞争状态。
在这里插入图片描述
原理:
当n个线程要访问我们临界区时,只允许一个线程进入,其余线程会阻塞在临界区外,每当一个线程运行完临界区代码后,会允许阻塞在临界区外的一个线程再次访问临界区。

3、lock()和try_lock()区别

  • lock():**lock 函数用于获取互斥锁。**如果当前互斥锁已被其他线程占用,则 lock 函数将阻塞当前线程,直到互斥锁可用为止。一旦成功获取到互斥锁,该线程将独占互斥锁,并可以继续执行后续的操作。在完成互斥锁所需的操作后,线程应该调用 unlock 函数来释放互斥锁,以便其他线程可以获取它。阻塞式的,会一直等待直到成功获取到互斥锁。

  • try_lock()try_lock 函数用于尝试获取互斥锁,但是它并不会阻塞当前线程。如果当前互斥锁可用,try_lock 函数将立即获取到互斥锁,并返回 true。如果当前互斥锁被其他线程占用,try_lock 函数将立即返回 false,而不会阻塞线程。通过检查返回值,线程可以根据情况选择等待或放弃获取互斥锁。非阻塞式的,立即返回获取互斥锁的结果,线程可以根据返回值做出相应的处理。
    例如:这里有10个线程,只有两个线程进入,而其它8个线程并不会阻塞住。
    在这里插入图片描述

  • 使用 lock 函数时要注意避免死锁的情况,而 try_lock 函数则可以更灵活地处理互斥锁的获取失败情况。

4、互斥锁的坑(线程抢占)

在这里插入图片描述
可以看到在这段代码中,本来1号线程运行完,执行unlock()后,应该其他阻塞的线程应该进入代码内,但是发现进入的依旧是1号ID的线程。
原因:当我们执行完unlock()后,因为代码执行速度就几微秒的时间,但是我们CPU调度探测一次得过一段时间,所以导致了这个问题。(也就是说我们执行的速度快于检测的速度,导致我们线程1号执行完后又再次进入循环,又把资源锁上了)
解决:只需要在unlock()后面等待一段时间就可以,让我们CPU有充裕的时间来调度检测。在这里插入图片描述

三、互斥锁(mutex)

上面样例中设计到这里就不在过多描述

四、超时锁(timed_mutex)

在这里插入图片描述
在这里插入图片描述

超时锁可以让阻塞的线程不断尝试进入,如果超过等待时间,就会执行日志结果,可以帮助我们更好的检查线程状态,检查是否超时或者是否存在死锁。

五、可重入锁(recursive_mutex)

1.可重入锁可以解决哪类情况?

如果直接用互斥锁在这里插入图片描述
显然会直接报错,因为我们要进入下一个锁内需要先解除当前锁在这里插入图片描述

显然是让人头疼麻烦的,我们必须每次得考虑解锁的时机。为了解决此类情况,我们有了可重入锁。

2.可重入锁。

在这里插入图片描述
功能如可以多重进入锁区域,要注意的是,进入几个锁就要在最后解除几个锁,这样才能让其他阻塞线程进入。

六、共享锁(shared_mutex)

C++ 14中引入了shared_timed_mutex;
C++ 17中引入了shared_mutex;

1、 共享锁的功能

假设有6个线程同时要访问同一片区域数据,有5个线程要读数据,而有一个线程要写数据,那么共享锁可以让读线程的5个线程同时进行读,然后阻塞写数据的线程。当写线程进入时,阻塞5个读的线程,写线程开始写数据。这就是共享锁的基本作用。

2、代码

#include<iostream>
#include<thread>
#include<shared_mutex>
using namespace std;
shared_mutex smux;
int num;
void thread_read(int i)
{
	for (;;)
	{
	smux.lock_shared();
	cout << "num is :" <<num<< endl;
	this_thread::sleep_for(1000ms);
	smux.unlock_shared();
	this_thread::sleep_for(10ms);
	}
}
void thread_write(int i)
{
	for (;;)
	{
	smux.lock_shared();
	num++;
	cout << "add num" << endl;
	this_thread::sleep_for(1000ms);
	smux.unlock_shared();
	this_thread::sleep_for(10ms);
	}

}

int main()
{
	for (int i = 0; i < 5; i++)
	{
		thread th(thread_read,i + 1);
		this_thread::sleep_for(50ms);
		th.detach();
	}
	for (int i = 0; i < 2; i++)
	{
		thread th(thread_write, i + 1);
		this_thread::sleep_for(50ms);
		th.detach();
	}
	getchar();
	return 0;
}

在这里插入图片描述

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

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

相关文章

用 25,000 颗星撬动一个向量数据库的世界

25,000 颗星是什么&#xff1f;是 GitHub 上对于一个数据库项目的极高认可&#xff0c;是 10,000 社区用户的共同协作&#xff0c;也是 5000 企业级用户的信赖与支持。 Milvus 用这 25,000 颗星撬动了向量数据库的世界&#xff0c;在 AGI 浪潮奔涌而来的时代&#xff0c;向全球…

Linux 内核被冬季风暴 “封印“

Linus Torvalds在内核邮件列表上宣布&#xff0c;由于他所在的美国俄勒冈州波特兰地区受到严重冬季风暴的影响&#xff0c;导致网络和电力中断。波特兰及其周边地区气温急降至零下 -10C&#xff0c;因此他不得不临时中断对Linux 6.8内核的合并窗口操作。 Linus于1月7日发布了Li…

智谱AI技术开放日:新一代基座大模型GLM-4及GLMs的发布

2024年1月16日&#xff0c;智谱AI举行了一次重要的技术开放日&#xff0c;发布了新一代基座大模型GLM-4和定制化的大模型GLMs。此次发布标志着智谱AI在人工智能领域的新一轮突破&#xff0c;进一步提升了大模型的性能&#xff0c;并降低了使用门槛&#xff0c;使得更多的人能够…

HCIP -- 第六天作业

要求&#xff1a; 实现&#xff1a; 3路由策略干涉选路&#xff1a;[R3]ip ip-prefix c permit 13.1.1.0 24 抓住13网段 [R3]route-policy c permit node 10 创建路由策略为C 序号为10 [R3-route-policy]if-match ip-prefix c 匹配路由策略c [R3-route-policy]apply cost-type…

《深入分析Linux内核源代码》读后感 --所有的进程都使用相同的逻辑地址空间,那么不同的进程是如何区分自己的数据段和代码段的呢

书中讲到从2.2版开始&#xff0c;Linux让所有的进程&#xff08;或叫任务&#xff09;都使用相同的逻辑地址空间&#xff0c;每个进程的逻辑地址空间范围为0&#xff5e;4GB&#xff0c;而且段基址也一样。那么不同的进程是如何区分自己的数据段和代码段的呢。看到另外一篇博文…

FFmpeg之AVFormat

文章目录 一、概述二、解封装流程三、重要结构体3.1、AVFormatContext3.2、AVInputFormat3.3、AVOutputFormat3.4、AVStream 四、重要函数分析4.1、avformat_alloc_context4.2、avformat_open_input4.2.1、init_input4.2.2、av_probe_input_format2 4.3、avformat_find_stream_…

RAG基础功能优化、以及RAG架构优化

RAG基础功能优化 对RAG的基础功能优化&#xff0c;我们要从RAG的流程入手[1]&#xff0c;可以在每个阶段做相应的场景优化。 从RAG的工作流程看&#xff0c;能优化的模块有&#xff1a;文档块切分、文本嵌入模型、提示工程优化、大模型迭代。下面针对每个模块分别做说明&#…

【C++】vector的使用及模拟实现

目录 一、vector的介绍及使用1.1 介绍vector1.2 vector的使用1.2.1 构造1.2.2 遍历访问1.2.3 容量空间1.2.4 增删查改 二、vector的模拟实现2.1 成员变量2.2 迭代器相关函数2.3 构造-析构-赋值重载2.3.1 无参构造2.3.2 有参构造12.3.3 有参构造22.3.4 拷贝构造2.3.5 赋值重载2.…

【线路图】 DC-DC升压恒压控制驱动芯片 2.8-40V AP8100

说明 AP8100 是一款外围电路简单的 BOOST 升压恒压控 制驱动芯片&#xff0c;适用于 2.8-40V 输入电压范围的升压恒 压电源应用领域&#xff0c;启动电压可以低至 2.5V 。 芯片会根据负载的大小自动切换 PWM &#xff0c; PFM 和 BURST 模式以提高各个负载端的…

【U盘修复】

U盘当成重装系统的U盘启动器之后&#xff0c;可能会从128G显示为 32 G&#xff0c;并且Windows自带的分区工具不管用&#xff0c;其实并不是U盘坏了&#xff0c;此时你需要将此U盘的所有分区删除&#xff0c;然后创建新的分区。 推荐使用的一款分区&#xff08;Partition&…

02-python的基础语法-01python字面量/注释/数据类型/数据类型转换

字面量 在代码中&#xff0c;被写下来的固定的值&#xff0c;被称为字面量。 python中哪些值是可以被写出来的呢?又该如何写呢&#xff1f; 字符串&#xff1a;又称文本&#xff0c;是由任意数量的字符如中文&#xff0c;英文&#xff0c;各类符号&#xff0c;数字组成。 这…

一文解析 Copycat Dex与 Bitcat Dex的区别

Copycat Dex和 Bitcat Dex都带一个 Cat 并且都是衍生品协议&#xff0c;很多人都会误认为这两个是同一个项目&#xff0c;实际不然。它们是面向两个不同赛道、不同资产类型的衍生品项目。 Copycat Dex和 Bitcat Dex都是衍生品 DEX&#xff0c;它们最本质的区别主要在于&#xf…

前后端跨域问题

告别烦恼&#xff0c;彻底解决跨域问题的终极指南-chrome的安全进阶之路_chrom 强制跨域-CSDN博客

Linux内核架构和工作原理详解(二)

Linux内核体系结构简析简析 图1 Linux系统层次结构 最上面是用户&#xff08;或应用程序&#xff09;空间。这是用户应用程序执行的地方。用户空间之下是内核空间&#xff0c;Linux 内核正是位于这里。GNU C Library &#xff08;glibc&#xff09;也在这里。它提供了连接内核…

软件测试要学习的基础知识——白盒测试

白盒测试是通过检查软件内部的逻辑结构&#xff0c;对软件中的逻辑路径进行覆盖测试&#xff0c;以确定实际运行状态与预期状态是否一致。 白盒测试又被称为&#xff1a; 透明盒测试 结构化测试 逻辑驱动测试 基于代码的测试 白盒测试的常用技术分类 一、静态分析&#x…

LLM之LangChain(二)| LangChain中的Agent

在本文中&#xff0c;我们将讨论LangChain中的Agent及其各种类型。但在深入研究Agent之前&#xff0c;让我们先了解一下什么是LangChain和Agent。 一、什么是LangChain&#xff1f; LangChain是一种功能强大的自动化工具&#xff0c;可用于各种任务&#xff0c;它提供了可用于…

【AIGC】智能革命的动物寓言——颠覆性的智慧之美

AIGC动物提示词绘画技巧 利用AIGC&#xff08;Artificial Intelligence Generated Content&#xff0c;人工智能生成内容&#xff09;技术进行绘画创作时&#xff0c;可以结合上述关键信息来设计和绘制不同风格的角色。具体步骤和讲解如下&#xff1a; 输入关键信息与风格设定…

C语言——详解字符函数和字符串数组(上)

目录 一、strlen的使用和模拟实现 1.strlen()函数的介绍 2.strlen()函数的具体使用 3.strlen函数的注意事项 4.strlen函数的模拟实现 二、strcpy的使用和模拟实现 1.strcpy()函数的介绍 2.strcpy()函数的具体使用 3.strcpy()函数的注意事项 4.strcpy函数的模拟实现 …

数据库概述、部署MySQL服务、必备命令 、密码管理、安装图形软件、SELECT语法 、筛选条件

1 案例1&#xff1a;构建MySQL服务器 1.1 问题 在IP地址192.168.88.50主机和192.168.88.51主机上部署mysql服务练习必备命令的使用 1.2 方案 准备2台虚拟机&#xff0c;要求如下&#xff1a; 1.3 步骤 实现此案例需要按照如下步骤进行。 步骤一&#xff1a;安装软件 命令…

2024.1.16 网络编程 作业

思维导图 练习题 1.基于UDP的TFTP文件传输&#xff0c;实现文件下载上传 #include <myhead.h>int main(int argc, char const *argv[]) {// 创建套接字UDP通信int sockfd socket(AF_INET, SOCK_DGRAM, 0);if (-1 sockfd){perror("socket error");return -1…