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

news2024/12/26 11:06:50

线程的互斥

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

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. 解除死锁:
◦ 一旦检测到死锁,就采取相应的措施解除死锁。例如,可以剥夺某些进程的资源,或者让一些进程回滚到之前的状态,释放它们所占用的资源。

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

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

相关文章

Axure RP 9高手速成秘籍:解锁终极快捷键,设计效率飙升10倍!

Axure RP 9作为一款功能强大的原型设计工具&#xff0c;提供了丰富的快捷键来加速设计流程。以下是一份详尽的Axure RP 9快捷键大全&#xff0c;旨在帮助用户更高效地完成设计工作。 一、文件操作 新建&#xff1a;Ctrl N&#xff08;Windows&#xff09;/ Command N&#…

【LLM大模型】本地玩转多模态Llama3

一般开源的LLM&#xff0c;例如Llama3和Qwen2等&#xff0c;只支持文本的输入&#xff0c;只能理解文本的内容&#xff0c;实现基于文本的逻辑推理和意图识别。 但是一些Chatbot&#xff0c;例如GPT-4V&#xff0c;就是拥有视觉能力&#xff0c;能够理解图片内容&#xff0c;能…

【LeetCode Cookbook(C++ 描述)】一刷二叉搜索树

目录 LeetCode #700&#xff1a;Search in a Binary Search Tree 二叉搜索树中的搜索递归法迭代法 LeetCode #98&#xff1a;Validate Binary Search Tree 验证二叉搜索树递归法迭代法 LeetCode #530&#xff1a;Minimum Absolute Difference in BST 二叉搜索树的最小绝对差递归…

在IEDA里打包Maven项目记录

之前在网上查找到的方式发现比较繁琐&#xff0c;所以把自己的解决办法记录一下分享给兄弟们 <plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-shade-plugin</artifactId><version>3.2.4</vers…

KT来袭,打造沉浸式体验的聚合性web3应用平台

随着步入 2024&#xff0c;漫长的区块链熊市即将接近尾声。纵观产业发展&#xff0c;逆流而上往往会是彰显品牌市场影响力和技术实力的最佳证明。在这次周期中&#xff0c;一个名为KT的web3.0聚合平台吸引了市场关注&#xff0c;无论在市场层面还是技术层面&#xff0c;都广泛赢…

【计算机三级-数据库技术】数据库后台编程技术

内容提要 1、掌握存储过程的定义与使用 2、掌握用户定义函数的创建与使用 3、掌握触发器的定义与使用 4、掌握游标的定义与使用 第一节 存储过程 使用T-SQL语言编写&#xff0c;有两种方式存储&#xff1a; 在客户端存储代码通过客户端程序或SQL命令向DBMS发出操作请求&…

JavaScript中设置器和获取器

在JavaScript中&#xff0c;setters 和 getters 是对象属性的特殊方法&#xff0c;用于定义如何访问和设置对象的属性。这些方法使得可以在对对象属性执行读取或写入操作时添加自定义逻辑。 举例 首先我们定义一个类似之前银行家的一个对象 const account {owner: ITshare,…

【Redis】数据结构和内部编码

数据结构和内部编码 type 命令实际返回的就是当前键的数据结构类型&#xff0c;它们分别是&#xff1a;string&#xff08;字符串&#xff09;、list&#xff08;列表&#xff09;、hash&#xff08;哈希&#xff09;、set&#xff08;集合&#xff09;、zset&#xff08;有序集…

模拟笔试 - 卡码网周赛第三十一期(23年百度笔试真题)

难度适中&#xff0c;动态规划出现的比例还是比较高的&#xff0c;要好好掌握&#xff0c;二分查找的点也是比较灵活的。&#xff08;A卷和B卷第一道题是一样的&#xff09; 题目一&#xff1a;讨厌鬼的组合帖子 思路&#xff1a;这个题算是一个还不错的题&#xff1b; 本质就…

ES 支持乐观锁吗?如何实现的?

本篇主要介绍一下Elasticsearch的并发控制和乐观锁的实现原理&#xff0c;列举常见的电商场景&#xff0c;关系型数据库的并发控制、ES的并发控制实践。 并发场景 不论是关系型数据库的应用&#xff0c;还是使用Elasticsearch做搜索加速的场景&#xff0c;只要有数据更新&…

Java导出分类到Excel

需求 在一般需求中点击导出按钮可以把所有的分类导出到Excel文件中。 技术方案 使用EasyExcel实现Excel的导出操作。 https://github.com/alibaba/easyexcel https://easyexcel.opensource.alibaba.com/docs/current/quickstart/write#%E7%A4%BA%E4%BE%8B%E4%BB%A3%E7%A0%81…

【计算机方向】三本中科院SCI宝刊,国人占比高,发文友好,门槛低,毕业靠它了!

本期将为您带来五本计算机SCI 妥妥毕业神刊&#xff01; ARTIFICIAL INTELLIGENCE REVIEW AUTONOMOUS ROBOTS Cognitive Computation 期刊名称&#xff1a;ARTIFICIAL INTELLIGENCE REVIEW 期刊简介&#xff1a; 发布对该领域的应用、技术和算法的批判性评估。 为研究人员…

HDU1159——通用子序列,HDU1160——FatMouse的速度、HDU1165——艾迪的研究 II

HDU1159——通用子序列 题目描述 问题 - 1159 (hdu.edu.cn) 问题描述 给定序列的子序列是给定的序列&#xff0c;其中遗漏了一些元素&#xff08;可能没有&#xff09;。给定一个序列 X <x1&#xff0c; x2&#xff0c; ...&#xff0c; xm>如果存在一个严格递增的 X …

【C++ Primer Plus习题】2.7

问题: 解答: #include <iostream> using namespace std;void print(int hour, int minute) {cout << "Time:" << hour << ":" << minute << endl; }int main() {int hour0;int minute 0;cout << "请输入…

NumExpr加速计算(numpy表达式)

文章目录 一、简介二、安装三、函数详解四、性能评估 Python 性能优化&#xff1a;NumExpr Numba CuPy 一、简介 numexpr&#xff08;全称&#xff1a;numpy expression&#xff09;&#xff1a;用于在 NumPy 表达式上快速执行元素级运算的 Python 加速库。 优势&#xff1…

软考高级科目怎么选?

首先上图 从图片中可以看出来&#xff0c;在软件开发中考试方向为程序员-软件设计师-系统架构师或者系统分析师。 系统分析师与系统架构师工作内容&#xff1a; 系统分析师&#xff1a;在信息系统项目开发过程中负责制定信息系统需求规格说明书和项目开发计划、指导和协调信息…

在网站文章中,‌<br>标签对SEO的影响及优化策略

在网页设计和内容创作中&#xff0c;‌<br>标签常被用于实现文本的换行显示。‌然而&#xff0c;‌对于关注SEO&#xff08;‌搜索引擎优化&#xff09;‌的网站管理员和内容创作者来说&#xff0c;‌<br>标签的使用却需要更加谨慎。‌这是因为<br>标签对SEO…

Linux系统编程全面学习

应用层&#xff1a;写一个QT可执行程序、一个C程序 驱动层&#xff1a;写一个LED、蜂鸣器、pwm驱动 硬件层&#xff1a;焊接、layout Linux系统介于应用层和驱动层之间&#xff0c;Linux系统会向应用层提供接口&#xff0c;学习使用的基本是Linux内核向用户提供的接口或者可以…

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

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

java 8种基础数据类型

1、数据范围 2、各个类型转换 实线转换&#xff1a;无信息丢失的自动转换&#xff0c;反方向需要强制类型转换&#xff0c;如&#xff08;int) 虚线转换&#xff1a;可能存在精度丢失 精度丢失示例如下&#xff1a; long l 123456787654321L; float f l; System.out.prin…