嵌入式学习——(Linux高级编程——线程控制)

线程的互斥

一、互斥的重要性
在多线程编程中,互斥机制至关重要。当多个线程同时访问临界资源时,如果没有有效的互斥控制,可能会导致数据不一致、资源竞争等问题。通过互斥锁,可以确保在任何时刻只有一个线程能够访问临界资源,从而保证程序的正确性和稳定性。
二、互斥锁的使用步骤详解

1. 定义锁:

pthread_mutex_t mutex;

定义了一个互斥锁变量。这个变量将在后续的步骤中被初始化、加锁、解锁和销毁。

2. 初始化锁:

int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);

函数用于将定义好的互斥锁进行初始化。
◦ 参数中的mutex是要初始化的互斥锁,而attr通常设置为NULL,表示使用默认的锁属性。
◦ 成功返回 0,失败返回非零值。

3. 加锁:

int pthread_mutex_lock(pthread_mutex_t *mutex);

函数用于对指定的互斥锁进行加锁操作。
◦ 加锁后的代码到解锁部分的代码被视为原子操作,这意味着在这个范围内的代码不会被其他线程中断。
◦ 如果在执行该函数时,互斥锁已经被其他线程占用,那么当前线程将被阻塞,直到互斥锁被释放。
◦ 成功返回 0,失败返回非零值。

4. 解锁:

int pthread_mutex_unlock(pthread_mutex_t *mutex);

函数用于将指定的互斥锁解锁。
◦ 解锁后,代码不再处于排他访问状态,其他线程可以竞争获取互斥锁来访问临界资源。
◦ 通常加锁和解锁操作会成对出现,以确保临界资源的正确访问。
◦ 成功返回 0,失败返回非零值。

5. 销毁:

int pthread_mutex_destroy(pthread_mutex_t *mutex);

函数在使用互斥锁完毕后用于销毁互斥锁。
◦ 成功返回 0,失败返回非零值。

6. trylock:

int pthread_mutex_trylock(pthread_mutex_t *mutex);

函数的功能类似于加锁函数,但它不会阻塞当前线程。
◦ 如果互斥锁可用,则该函数会成功加锁并返回 0;如果互斥锁不可用,则返回非零值,通常是E_AGAIN。

三、使用互斥锁的注意事项
1. 框架设计时应尽量使保护区尽可能短,以减少线程阻塞的时间,提高程序的性能。
2. 在使用互斥锁时,要确保加锁和解锁操作的成对出现,避免出现死锁等问题。
3. 对于互斥锁的初始化和销毁操作,要确保在正确的时机进行,避免资源泄漏。

在这段代码中,互斥锁的作用是确保多个线程对全局变量 A 的自增操作是原子性的,即不会被其他线程中断。如果没有互斥锁,两个线程可能会同时读取 A 的值,然后进行自增操作,这可能会导致结果不一致。通过使用互斥锁,在一个线程对 A 进行操作时,其他线程必须等待,直到该线程完成操作并解锁互斥锁。 

练习:模拟了十个人去银行办理业务,银行有三个办理窗口的场景。通过使用互斥锁来确保对共享资源(窗口数量)的安全访问,避免出现资源竞争问题。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

pthread_mutex_t mutex;

int WIN = 3;	//三个办理窗口

void* th(void* arg)
{
	while(1)
	{
		pthread_mutex_lock(&mutex);		//要使用共享资源,加锁
		if ( WIN > 0 )			//有窗口
		{
			WIN--;				//占用窗口
			pthread_mutex_unlock(&mutex);	//解锁
			printf("get WIN tid:%lu\n",pthread_self());		//表示在用窗口
			sleep(rand()%5);				//办理中
			printf("relese WIN tid:%lu\n",pthread_self());	//表示离开
			pthread_mutex_lock(&mutex);
			WIN++;							//离开后空出窗口
			pthread_mutex_unlock(&mutex);
			break;							
		}
		else			//没有窗口的情况下
		{
			pthread_mutex_unlock(&mutex);
		}
	}
	return NULL;
}

int main(int argc, const char *argv[])
{
	pthread_t tid[10];	//十个人去银行办业务

	pthread_mutex_init(&mutex,NULL);	//初始化互斥锁
	int i;
	for ( i=0; i<10; i++ )
	{
		pthread_create(&tid[i],NULL,th,NULL);
	}

	for ( i=0; i<10; i++ )
	{
		pthread_join(tid[i],NULL);
	}
	pthread_mutex_destroy(&mutex);		//销毁互斥锁
	return 0;
}

void* th(void* arg)是线程执行的函数。在这个函数中,通过一个无限循环不断尝试获取办理窗口。首先加锁,然后检查窗口数量是否大于 0。如果有窗口可用,占用一个窗口,解锁,打印获取窗口的线程 ID,表示正在使用窗口,然后随机睡眠一段时间模拟办理业务,最后再次加锁,释放窗口,增加窗口数量,解锁并退出循环。如果没有窗口可用,直接解锁。

在这段代码中,互斥锁的作用是确保多个线程在访问和修改共享资源(窗口数量)时不会出现数据不一致的情况。当一个线程检查窗口数量、占用窗口或释放窗口时,其他线程必须等待,直到该线程完成这些操作并解锁互斥锁。这样可以保证窗口数量的正确更新,避免出现多个线程同时占用同一个窗口或者窗口数量错误减少或增加的情况。

线程的同步

一、线程同步的重要性与信号量的作用
在多线程编程中,线程的同步是确保程序正确运行的关键。单纯的互斥锁只能控制对资源的排他性访问,但无法保证线程执行的先后顺序。而信号量机制则通过引入 “有一定先后顺序的对资源的排他性访问”,有效地解决了这个问题,实现了线程之间的同步。
信号量分为无名信号量和有名信号量,分别用于线程间通信和进程间通信,为不同场景下的同步需求提供了灵活的解决方案。
二、信号量使用步骤详解

1. 信号量的定义:

sem_t sem;

定义了一个信号量变量。这个变量将在后续的步骤中被初始化、进行 PV 操作以及销毁。

2. 信号量的初始化:

int sem_init(sem_t *sem, int pshared, unsigned int value);

函数用于将定义好的信号量进行赋值初始化。
◦ 当pshared = 0时,表示用于线程间使用信号量;当pshared!= 0时,表示用于进程间使用信号量。
◦ 对于无名信号量通常是二值信号量,初始值为 0 或 1。0 表示红灯,线程暂停阻塞;1 表示绿灯,线程可以通过执行。
◦ 成功返回 0,失败返回 -1。

3. 信号量的 PV 操作:

◦ P 操作(申请资源):

int sem_wait(sem_t *sem);

函数判断当前信号量是否有资源可用。如果有资源(值为 1),则申请该资源,程序继续运行,并且信号量的值会自动减 1。如果没有资源(值为 0),则线程阻塞等待,一旦有资源则自动申请资源并继续运行程序。成功返回 0,失败返回 -1。
◦ V 操作(释放资源):

int sem_post(sem_t *sem);

函数可以将指定的信号量资源释放,并默认执行信号量值加 1 的操作。线程在该函数上不会阻塞。成功返回 0,失败返回 -1。

4. 信号量的销毁:

int sem_destroy(sem_t *sem);

函数在使用完毕后将指定的信号量销毁。成功返回 0,失败返回 -1。

三、使用信号量的注意事项
1. 在使用信号量进行线程同步时,要确保正确初始化信号量的初始值和参数,以满足具体的同步需求。
2. 在进行 PV 操作时,要注意操作的顺序和时机,避免出现死锁等问题。
3. 对于信号量的销毁操作,要确保在所有线程都不再使用该信号量时进行,避免资源泄漏。

二值信号量 

在这段代码中,二值信号量sem_H和sem_W起到了控制线程执行顺序的关键作用。初始状态下,sem_H为 1,使得线程 1 首先执行,打印 “Hello” 后释放sem_W,从而唤醒线程 2。线程 2 打印 “World” 后释放sem_H,又回到线程 1 执行,如此循环交替,确保 “Hello” 和 “World” 按照特定的顺序输出。 

计数信号量

在这段代码中,计数信号量 WIN 的初始值为 3,表示有三个可用资源。多个线程可以同时竞争这些资源,当一个线程获取到资源时,信号量的值会减 1;当一个线程释放资源时,信号量的值会加 1。这样可以确保同时使用资源的线程数量不超过信号量的初始值,从而实现对有限资源的并发访问控制。 

 

死锁

一、死锁的危害
死锁是多线程或多进程系统中一种严重的问题,它会导致系统资源被占用却无法释放,使得系统无法正常运行。如果死锁频繁发生,可能会造成系统性能严重下降甚至完全瘫痪,影响到整个应用程序的可用性和可靠性。
二、死锁产生原因详解
1. 系统资源不足:
◦ 当系统中的资源数量有限,而多个进程或线程同时竞争这些资源时,如果每个进程或线程都无法获取到所需的全部资源,就可能陷入死锁状态。例如,有两个进程都需要两种资源 A 和 B,而系统中只有一个 A 资源和一个 B 资源,那么如果一个进程获取了 A 资源,另一个进程获取了 B 资源,它们就会相互等待对方释放资源,从而导致死锁。
2. 进程运行推进顺序不合适:
◦ 进程的执行顺序对于避免死锁至关重要。如果进程以不恰当的顺序请求和释放资源,就可能形成死锁。例如,进程 P1 先请求资源 A,再请求资源 B,而进程 P2 先请求资源 B,再请求资源 A。如果 P1 获得了 A,P2 获得了 B,那么它们就会相互等待,从而产生死锁。
3. 资源分配不当等:
◦ 不合理的资源分配策略也可能导致死锁。例如,如果资源分配算法总是优先满足某些进程的请求,而忽略其他进程的需求,就可能导致部分进程永远无法获取到所需资源,从而陷入死锁。
三、死锁的四个必要条件分析
1. 互斥条件:
◦ 这是死锁产生的基本条件之一。如果资源可以同时被多个进程或线程使用,那么就不会出现死锁。例如,多个进程可以同时读取一个文件,而不会产生死锁。但是,如果资源是排他性的,即一次只能被一个进程或线程使用,那么就有可能出现死锁。
2. 请求与保持条件:
◦ 当一个进程因请求资源而阻塞时,如果它仍然保持着已经获得的资源,就可能导致死锁。例如,一个进程已经获得了资源 A,现在又请求资源 B,但由于 B 资源不可用而被阻塞。此时,该进程仍然持有资源 A,而其他需要资源 A 的进程就无法继续执行,从而可能形成死锁。
3. 不剥夺条件:
◦ 进程已获得的资源在未使用完之前不能被强行剥夺,这也是死锁产生的一个重要条件。如果可以强行剥夺进程的资源,那么当一个进程因请求新资源而被阻塞时,系统可以剥夺它已获得的资源,分配给其他需要的进程,从而避免死锁的发生。
4. 循环等待条件:
◦ 若干进程之间形成一种头尾相接的循环等待资源关系是死锁的典型特征。例如,进程 P1 等待进程 P2 释放资源,进程 P2 等待进程 P3 释放资源,进程 P3 又等待进程 P1 释放资源,这样就形成了循环等待,必然导致死锁。
四、避免死锁的方法
1. 预防死锁:
◦ 通过破坏死锁的四个必要条件之一来避免死锁的发生。例如,可以采用资源静态分配策略,破坏请求与保持条件;或者采用资源剥夺策略,破坏不剥夺条件。
2. 避免死锁:
◦ 在资源分配过程中,通过动态地检测系统状态,确保资源分配不会导致死锁的发生。例如,可以使用银行家算法,在分配资源之前先判断系统是否处于安全状态。
3. 检测死锁:
◦ 定期检测系统中是否存在死锁,如果发现死锁,则采取相应的措施进行解除。例如,可以通过资源分配图等方法检测死锁。
4. 解除死锁:
◦ 一旦检测到死锁,就采取相应的措施解除死锁。例如,可以剥夺某些进程的资源,或者让一些进程回滚到之前的状态,释放它们所占用的资源。

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

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

相关文章

PHPShort轻量级网址缩短程序源码开心版,内含汉化包

需要网址缩短并且想获得更多有关链接点击率和流量的数据分析&#xff0c;那么 PHPShort 可能是一个非常好的选择。PHPShort 是一款高级的 URL 缩短器平台&#xff0c;可以帮助你轻松地缩短链接&#xff0c;并根据受众群体的位置或平台来定位受众。 该程序基于 Laravel 框架编写…

python构建一个web程序

from flask import Flaskapp Flask(__name__)app.route(/) def hello_world():return 欢迎来到我的Python Web程序!if __name__ __main__:app.run(debugTrue)1、安装flask D:\Users\USER\PycharmProjects\pythonProject1\p01>pip install flask WARNING: Ignoring invalid…

多线程中常见问题

1、为什么不建议使用Executors来创建线程池&#xff1f; 除开有可能造成的OOM外&#xff0c;使用Executors来创建线程池也不能自定义线程的名字&#xff0c;不利于排查问题&#xff0c;所以建议是直接使用ThreadPoolExecutor来定义线程池&#xff0c;这样可以灵活控制 2、线程…

2 种方式申请免费 SSL 证书,阿里云 Certbot

如何使用免费的 SSL 证书&#xff0c;有时在项目中需要使用免费的 SSL 证书&#xff0c;Aliyun 提供免费证书&#xff0c;三个月有效期&#xff0c;可以直接在aliyun 申请&#xff0c;搜索 SSL 证书&#xff0c;选择测试证书。 Aliyun 证书需要每三月来来换一次&#xff0c;页…

线程优先级调度

Windows优先级调度算法 系统维护了一个全局的处理器数组KiProcessorBlock&#xff0c;其中每个元素对应于一个处理器的KPRCB对象。其次&#xff0c;另有一个全局变量KiIdleSummary记录了哪些处理器当前是空闲的。所谓一个处理器是空闲的&#xff0c;是指该处理器正在执行空闲循…

根据状态的不同,显示不同的背景颜色

文章目录 前言HTML模板部分JavaScript部分注意&#xff1a;主要差异影响如何处理示例 总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 实现效果&#xff1a; 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 根据给定的状态…

内网安全:跨域攻击

目录 获取域信息 利用域信任密钥获取目标域 利用krbtgt哈希值获取目标域 内网中的域林&#xff1a; 很多大型企业都拥有自己的内网&#xff0c;一般通过域林进行共享资源。根据不同职能区分的部门&#xff0c;从逻辑上以 主域和子域进行区分&#xff0c;以方便统一管理。在…

基于x86 平台opencv的图像采集和seetaface6的图像质量评估功能

目录 一、概述二、环境要求2.1 硬件环境2.2 软件环境三、开发流程3.1 编写测试3.2 配置资源文件3.2 验证功能一、概述 本文档是针对x86 平台opencv的图像采集和seetaface6的图像质量评估功能,opencv通过摄像头采集视频图像,将采集的视频图像送给seetaface6的图像质量评估模块…

大模型学习笔记 - LLM 之 attention 优化

LLM 注意力机制 LLM 注意力机制 1. 注意力机制类型概述2.Group Query Attention3.FlashAttention4. PageAttention 1. 注意力机制类型概述 注意力机制最早来源于Transformer&#xff0c;Transformer中的注意力机制分为2种 Encoder中的 全量注意力机制和 Decoder中的带mask的…

qt处理表格,Qtxlsx库文件的安装以及导入

qt想要处理excel表格的&#xff0c;这个过程中避免不了使用Qtxlsx这个库文件。这几天花了几天时间&#xff0c;终于本地调通了。记录一下。 关于Qtxlsx的使用&#xff0c;大致分为2中方法。 方法一&#xff1a;直接下载对应的xlsx文件&#xff0c;然后在.pro文件中 这种方法是…

《黑神话.悟空》:一场跨越神话与现实的深度探索

《黑神话.悟空》&#xff1a;一场跨越神话与现实的深度探索 在国产游戏日益崛起的今天&#xff0c;《黑神话.悟空》以其独特的剧情、丰富的人物设定和深刻的主题&#xff0c;成为了无数玩家翘首以盼的国产3A大作。这款游戏不仅是一次对传统故事的创新演绎&#xff0c;更是一场对…

《黑神话:悟空》游戏攻略:第一回合打法教程!

《黑神话&#xff1a;悟空》是一款以西游记为背景的动作角色扮演游戏&#xff0c;玩家在游戏中将面对各式各样的强大敌人和BOSS。在游戏的第一回合中&#xff0c;你将遇到牯护院、灵虚子、幽魂等多个BOSS。以下是详细的BOSS打法攻略&#xff0c;帮助你在战斗中游刃有余。你可以…

eNSP 华为三层交换机配置DHCP

华为三层交换机配置DHCP 华为DHCP原理&#xff1a;&#xff08;思科四个都是广播包&#xff09; 1、客户端广播发送DHCP Discover包。用于发现当前局域网中的DHCP服务器。 2、DHCP服务器单播发送DHCP Offer包给客户端。携带分配给客户端的IP地址。 3、客户端广播发送DHCP Resqe…

路径规划 | 灰狼算法+B样条曲线优化无人机三维路径规划(Matlab)

目录 效果一览基本介绍程序设计参考文献 效果一览 基本介绍 灰狼算法B样条曲线优化无人机三维路径规划&#xff08;Matlab&#xff09; 群智能路径规划算法。三维灰狼算法&#xff08;GWO&#xff09;加B样条曲线优化的matlab代码。无人机&#xff08;UAV&#xff09;路径规划…

理解Tomcat的IP绑定与访问控制

在使用Spring Boot开发应用时&#xff0c;内置的Tomcat容器提供了灵活的网络配置选项。特别是&#xff0c;当计算机上有多个网卡时&#xff0c;如何配置server.address属性显得尤为重要。本文将详细探讨不同IP配置对Tomcat服务访问的影响。 多网卡环境下的IP配置 假设你的计算…

入门redis

一、安装redis-py库 打开pycharm 在终端中输入 pip install redis 二、连接到redis服务器 import redis r redis.Redis(hostlocalhost, port6379, db0, decode_responsesTrue)host是 Redis 服务器的主机名或 IP 地址&#xff0c;port是端口号&#xff0c;db是要使用的数据库编…

高并发登录模块

1.配置MySQL5.7服务的一主二从 mycat对mysql8不完全⽀持 1. gtids事务复制 2. 删除/etc/my.cnf 3. 同步data⽂件需要先停⽤mysql服务&#xff0c;删除data⽬录中的 auto.cnf 4. gtid模式以及经典模式都需要锁表 flush tables with read lock; unlock tables; set global.read_…

C++设计模式1:单例模式(懒汉模式和饿汉模式,以及多线程问题处理)

饿汉单例模式 程序还没有主动获取实例对象&#xff0c;该对象就产生了&#xff0c;也就是程序刚开始运行&#xff0c;这个对象就已经初始化了。 class Singleton { public:~Singleton(){std::cout << "~Singleton()" << std::endl;}static Singleton* …

给跨行或同行转岗产品经理的几点建议

转岗不但要勇气还需要方法。现在&#xff0c;从其他职位转岗成为产品经理并不罕见。交互设计师&#xff0c;UI设计师&#xff0c;测试&#xff0c;开发&#xff0c;运营和用户研究都有大量转岗到产品经理的事例&#xff0c;甚至还有客户服务&#xff0c;销售转岗产品的。 一方面…

火爆国内外的《黑神话:悟空》,需要什么显卡才能玩?

一路西行&#xff0c;大圣归来&#xff01; 8月20日&#xff0c;国产游戏《黑神话&#xff1a;悟空》上午10时正式上线。这款游戏在Steam平台的同时在线玩家突破了114万&#xff0c;超越《CS2》登顶Steam热玩榜。 仅单日实际在线人数就超过了210万 &#xff0c;超过《幻兽帕鲁…