同步与互斥(二)

news2024/11/24 15:07:43

一、谁上锁就由谁解锁?

        互斥量、互斥锁,本来的概念确实是:谁上锁就得由谁解锁。
        但是FreeRTOS并没有实现这点,只是要求程序员按照这样的惯例写代码。
        main函数创建了2个任务:
        任务1:高优先级,一开始就获得互斥锁,永远不释放。
        任务2:任务1阻塞时它开始执行,它先尝试获得互斥量,失败的话就监守自盗(释放互斥量、开锁),然后再上锁

代码如下:

int main( void )
{
    prvSetupHardware();
    /* 创建互斥量 */
    xMutex = xSemaphoreCreateMutex( );
    if( xMutex != NULL )
    {
        /* 创建2个任务: 一个上锁, 另一个自己监守自盗(开别人的锁自己用)
        */
        xTaskCreate( vTakeTask, "Task1", 1000, NULL, 2, NULL );
        xTaskCreate( vGiveAndTakeTask, "Task2", 1000, NULL, 1, NULL );
        /* 启动调度器 */
        vTaskStartScheduler();
    }
    else
    {
        /* 无法创建互斥量 */
    }
    /* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
    return 0;
}

两个任务的代码和执行流程如下图所示:
A:任务1的优先级高,先运行,立刻上锁
B:任务1阻塞
C:任务2开始执行,尝试获得互斥量(上锁),超时时间设为0。根据返回值打印出:上锁失败

因为A流程那里没有解锁,所以会上锁失败,而且超时时间设置为0,不会等待,获取失败就直接返回BaseType_t,成功就返回的是pdTURE。
D:任务2监守自盗,开锁,成功!
E:任务2成功获得互斥量
F:任务2阻塞
        可见,任务1上的锁,被任务2解开了。所以,FreeRTOS并没有实现"谁上锁就得由谁开锁"的功能。

二、优先级反转

假设任务A、B都想使用串口,A优先级比较低:
        任务A获得了串口的互斥量
        任务B也想使用串口,它将会阻塞、等待A释放互斥量
高优先级的任务,被低优先级的任务延迟,这被称为"优先级反转"(priority inversion)
如果涉及3个任务,可以让"优先级反转"的后果更加恶劣。
        互斥量可以通过"优先级继承",可以很大程度解决"优先级反转"的问题,这也是FreeRTOS中互斥量和二级制信号量的差别。
        程序使用二级制信号量来演示"优先级反转"的恶劣后果。
        main函数创建了3个任务:LPTask/MPTask/HPTask(低/中/高优先级任务),代码如下:

/* 互斥量/二进制信号量句柄 */
SemaphoreHandle_t xLock;
int main( void )
{
    prvSetupHardware();
    /* 创建互斥量/二进制信号量 */
    xLock = xSemaphoreCreateBinary( );
    if( xLock != NULL )
    {
        /* 创建3个任务: LP,MP,HP(低/中/高优先级任务)
        */
        xTaskCreate( vLPTask, "LPTask", 1000, NULL, 1, NULL );
        xTaskCreate( vMPTask, "MPTask", 1000, NULL, 2, NULL );
        xTaskCreate( vHPTask, "HPTask", 1000, NULL, 3, NULL );
        /* 启动调度器 */
        vTaskStartScheduler();
    }
    else
    {
        /* 无法创建互斥量/二进制信号量 */
    }
    /* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
    return 0;
}

LPTask/MPTask/HPTask三个任务的代码和运行过程如下图所示:
A:HPTask优先级最高,它最先运行。在这里故意打印,这样才可以观察到flagHPTaskRun的脉
冲。
B:MPTask开始运行。在这里故意打印,这样才可以观察到flagMPTaskRun的脉冲。
C:LPTask开始运行,获得二进制信号量,然后故意打印很多字符
D:HP Delay时间到,HPTask恢复运行,它无法获得二进制信号量,一直阻塞等待

// #define portMAX_DELAY         ( TickType_t ) 0xffffffffUL

查看串口打印情况更加直观:


E:MP Delay时间到,MPTask恢复运行,它比LPTask优先级高,一直运行。导致LPTask无法运
行,自然无法释放二进制信号量,于是HPTask无法运行。
总结:
        LPTask先持有二进制信号量,但是MPTask抢占LPTask,使得LPTask一直无法运行也就无法释放信号量,导致HPTask任务无法运行,优先级最高的HPTask竟然一直无法运行!

static void vHPTask( void *pvParameters )
{
	const TickType_t xTicksToWait = pdMS_TO_TICKS( 10UL );	

	flagLPTaskRun = 0;
	flagMPTaskRun = 0;
	flagHPTaskRun = 1;

	printf("HPTask start\r\n");
	
	/* 让LPTask先运行 */	
	vTaskDelay(xTicksToWait);
	
	/* 无限循环 */
	for( ;; )
	{	
		flagLPTaskRun = 0;
		flagMPTaskRun = 0;
		flagHPTaskRun = 1;
		printf("HPTask wait for Lock\r\n");
		
		/* 获得互斥量/二进制信号量 */
		xSemaphoreTake(xLock, portMAX_DELAY);
		
		flagLPTaskRun = 0;
		flagMPTaskRun = 0;
		flagHPTaskRun = 1;
		
		/* 释放互斥量/二进制信号量 */
		xSemaphoreGive(xLock);
	}
}

static void vMPTask( void *pvParameters )
{
	const TickType_t xTicksToWait = pdMS_TO_TICKS( 30UL );	

	flagLPTaskRun = 0;
	flagMPTaskRun = 1;
	flagHPTaskRun = 0;

	printf("MPTask start\r\n");
	
	/* 让LPTask、HPTask先运行 */	
	vTaskDelay(xTicksToWait);
	
	/* 无限循环 */
	for( ;; )
	{	
		flagLPTaskRun = 0;
		flagMPTaskRun = 1;
		flagHPTaskRun = 0;
	}
}
static void vLPTask( void *pvParameters )
{
	const TickType_t xTicksToWait = pdMS_TO_TICKS( 10UL );	
	uint32_t i;
	char c = 'A';

	printf("LPTask start\r\n");
	
	/* 无限循环 */
	for( ;; )
	{	
		flagLPTaskRun = 1;
		flagMPTaskRun = 0;
		flagHPTaskRun = 0;

		/* 获得互斥量/二进制信号量 */
		xSemaphoreTake(xLock, portMAX_DELAY);
		
		/* 耗时很久 */
		
		printf("LPTask take the Lock for long time");
		for (i = 0; i < 26; i++) 
		{
			flagLPTaskRun = 1;
			flagMPTaskRun = 0;
			flagHPTaskRun = 0;
			printf("%c", c + i);
		}
		printf("\r\n");
		
		/* 释放互斥量/二进制信号量 */
		xSemaphoreGive(xLock);
		
		vTaskDelay(xTicksToWait);
	}
}

优先级继承

/* 互斥量/二进制信号量句柄 */
SemaphoreHandle_t xLock;

int main( void )
{
	prvSetupHardware();
	
    /* 创建互斥量/二进制信号量 */
    //xLock = xSemaphoreCreateBinary( );
	xLock = xSemaphoreCreateMutex( );


	if( xLock != NULL )
	{
		/* 创建3个任务: LP,MP,HP(低/中/高优先级任务)
		 */
		xTaskCreate( vLPTask, "LPTask", 1000, NULL, 1, NULL );
		xTaskCreate( vMPTask, "MPTask", 1000, NULL, 2, NULL );
		xTaskCreate( vHPTask, "HPTask", 1000, NULL, 3, NULL );

		/* 启动调度器 */
		vTaskStartScheduler();
	}
	else
	{
		/* 无法创建互斥量/二进制信号量 */
	}

	/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
	return 0;
}

/*-----------------------------------------------------------*/

/*-----------------------------------------------------------*/
static void vLPTask( void *pvParameters )
{
	const TickType_t xTicksToWait = pdMS_TO_TICKS( 10UL );	
	uint32_t i;
	char c = 'A';

	printf("LPTask start\r\n");
	
	/* 无限循环 */
	for( ;; )
	{	
		flagLPTaskRun = 1;
		flagMPTaskRun = 0;
		flagHPTaskRun = 0;

		/* 获得互斥量/二进制信号量 */
		xSemaphoreTake(xLock, portMAX_DELAY);
		
		/* 耗时很久 */
		
		printf("LPTask take the Lock for long time");
		for (i = 0; i < 26; i++) 
		{
			flagLPTaskRun = 1;
			flagMPTaskRun = 0;
			flagHPTaskRun = 0;
			printf("%c", c + i);
		}
		printf("\r\n");
		
		/* 释放互斥量/二进制信号量 */
		xSemaphoreGive(xLock);
		printf("task_low\r\n");
		vTaskDelay(xTicksToWait);
	}
}

static void vMPTask( void *pvParameters )
{
	const TickType_t xTicksToWait = pdMS_TO_TICKS( 30UL );	

	flagLPTaskRun = 0;
	flagMPTaskRun = 1;
	flagHPTaskRun = 0;

	printf("MPTask start\r\n");
	
	/* 让LPTask、HPTask先运行 */	
	vTaskDelay(xTicksToWait);
	
	/* 无限循环 */
	for( ;; )
	{	
		flagLPTaskRun = 0;
		flagMPTaskRun = 1;
		flagHPTaskRun = 0;
	}
}

static void vHPTask( void *pvParameters )
{
	const TickType_t xTicksToWait = pdMS_TO_TICKS( 10UL );	

	flagLPTaskRun = 0;
	flagMPTaskRun = 0;
	flagHPTaskRun = 1;

	printf("HPTask start\r\n");
	
	/* 让LPTask先运行 */	
	vTaskDelay(xTicksToWait);
	
	/* 无限循环 */
	for( ;; )
	{	
		flagLPTaskRun = 0;
		flagMPTaskRun = 0;
		flagHPTaskRun = 1;
		printf("HPTask wait for Lock\r\n");
		
		/* 获得互斥量/二进制信号量 */
		xSemaphoreTake(xLock, portMAX_DELAY);
		printf("task_high\r\n");
		flagLPTaskRun = 0;
		flagMPTaskRun = 0;
		flagHPTaskRun = 1;
		
		/* 释放互斥量/二进制信号量 */
		xSemaphoreGive(xLock);
		printf("task\r\n");
	}
}

 唯一的改变就是:

运行时序图如下图所示:
A:HPTask执行xSemaphoreTake(xLock, portMAX_DELAY); ,它的优先级被LPTask继承
B:LPTask抢占MPTask,运行
C:LPTask执行xSemaphoreGive(xLock); ,它的优先级恢复为原来值
D:HPTask得到互斥锁,开始运行
互斥锁的"优先级继承",可以减小"优先级反转"的影响

 

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

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

相关文章

文件:文本文件和二进制文件 详解

目录 0 引言1 文本文件1.1 是如何存储的&#xff1f;1.2 文件拓展名 2 二进制文件2.1 是如何存储的&#xff1f;2.2 分类2.2.1 图像文件2.2.2 音频文件2.2.3 视频文件2.2.4 可执行文件 &#x1f64b;‍♂️ 作者&#xff1a;海码007&#x1f4dc; 专栏&#xff1a;C专栏&#x…

GD32F4中断向量查询

中断向量表 中断向量对应函数 __Vectors DCD __initial_sp ; Top of StackDCD Reset_Handler ; Reset HandlerDCD NMI_Handler ; NMI HandlerDCD HardFault_Handler ;…

行为型设计模式(三)状态模式 备忘录模式

状态模式 State 1、什么是状态模式 状态模式允许一个对象在其内部状态改变时改变它的行为&#xff0c;对象看起来似乎修改了它的类&#xff0c;将对象的行为包装在不同的状态类中&#xff0c;对象在运行时根据内部状态的改变而改变它的行为。 2、为什么使用状态模式 封装了…

【数字图像处理】实验四 图像分割

一、实验内容&#xff1a; 1&#xff0e; 熟悉和掌握利用Matlab工具进行数字图像的读、写、显示等数字图像处理基本步骤。 2&#xff0e; 熟练掌握各种图像分割的基本原理及方法。 3&#xff0e; 能够从深刻理解图像分割&#xff0c;并能够思考拓展到一定的应用领域。 二、实验…

雷军1992年发表的计算机论文,强啊!

提起雷军&#xff0c;相信大家都非常熟悉了&#xff0c;90年代初毕业于武大计算机系&#xff0c;后来一路创业&#xff0c;现如今在相应领域的成就也有目共睹。 之前咱们这里也分享过雷军早年间写的文字博客&#xff0c;他曾在早期博客中讲述了自己的编程之路&#xff0c;以及当…

机器学习 | 概率图模型

见微知著&#xff0c;睹始知终。 见到细微的苗头就能预知事物的发展方向&#xff0c;能透过微小的现象看到事物的本质&#xff0c;推断结论或者结果。 概率模型为机器学习打开了一扇新的大门&#xff0c;将学习的任务转变为计算变量的概率分布。 实际情况中&#xff0c;各个变量…

一个简单的 HTTP 请求和响应服务——httpbin

拉取镜像 docker pull kennethreitz/httpbin:latest 查看本地是否存在存在镜像 docker images | grep kennethreitz/httpbin:latest 创建 deployment&#xff0c;指定镜像 apiVersion: apps/v1 kind: Deployment metadata:labels:app: httpbinname: mm-httpbinnamespace: mm-…

FPC柔性排线用什么胶水能固定到线路板上?

为了固定FPC柔性排线到线路板上&#xff0c;可以使用特殊用于电子组装的胶水。常用的胶水类型有&#xff1a; 1.氰基丙烯酸酯胶水&#xff08;Cyanoacrylate&#xff09; 被称为“超级胶水”或“快干胶水”。这种胶水对FPC通常有很好的附着力。 2.环氧树脂胶水 环氧树脂胶水…

C++之深拷贝和浅拷贝

目录 浅拷贝 深拷贝 赋值运算符重载的深拷贝 在学习C类和对象时我们学习了浅拷贝&#xff0c;本期我们将再次回顾浅拷贝并为大家讲述深拷贝的概念。 浅拷贝 在学习类和对象时我们学习了拷贝构造函数的概念&#xff0c;而且我们也知道&#xff0c;因为拷贝构造函数属于类的默…

02|用LangChain快速构建基于“易速鲜花”本地知识库的智能问答系统

02&#xff5c;用LangChain快速构建基于“易速鲜花”本地知识库的智能问答系统 项目及实现框架 我们先来整体了解一下这个项目。 项目名称&#xff1a;“易速鲜花”内部员工知识库问答系统。 项目介绍&#xff1a;“易速鲜花”作为一个大型在线鲜花销售平台&#xff0c;有自…

SpringCloudGateway网关处拦截并修改请求

SpringCloudGateway网关处拦截并修改请求 需求背景 老系统没有引入Token的概念&#xff0c;之前的租户Id拼接在请求上&#xff0c;有的是以Get&#xff0c;Param传参形式&#xff1b;有的是以Post&#xff0c;Body传参的。需要在网关层拦截请求并进行请求修改后转发到对应服务。…

软件工程快速复习(期末急救)

每个同学要假想自己是一个项目经理&#xff0c;去完成一个软件项目&#xff0c;比如医院管理系统&#xff0c;自动设备控制系统等&#xff0c;以面向结构的软件工程方法&#xff0c;说出完成项目的步骤&#xff0c;涉及到的具体技术。初步了解面向对象的方法的与面向结构的方法…

Linux网络编程——字节序

一、概念 字节序是指多字节数据在计算机内存中存储或者网络传输时各字节的存储顺序。 二、分类 字节序有两者常见序&#xff1a; 1. Big-Endian&#xff08;大端&#xff09;&#xff1a;高位字节排放在内存的低地址端&#xff0c;低位字节排放在内存的高地址端。如&#x…

量化交易学习笔记:XGBoost 在量化选股中的应用

一、引言 本篇文章通过借鉴传统机器学习算法——XGBoost——对相同的量价因子进行实验&#xff0c;方便与深度学习模型进行对比实践。 二、算法介绍 XGBoost 是在 Gradient Boosting&#xff08;梯度提升&#xff09;框架下实现的机器学习算法&#xff0c;全称为“极限梯度提…

图神经网络并在 TensorFlow 中实现

asokraju.medium.com 一、说明 本文将引导您了解图神经网络 (GNN) 并使用 TensorFlow 实现该网络。在后续的 文章中&#xff0c;我们讨论 GNN 的不同变体及其实现。这是一个分步计划&#xff1a; 图神经网络 (GNN) 的使用&#xff1a;我们首先讨论 GNN 是什么、它们如何工作以及…

论文阅读<MULTISCALE DOMAIN ADAPTIVE YOLO FOR CROSS-DOMAIN OBJECT DETECTION>

论文链接&#xff1a;https://arxiv.org/pdf/2106.01483v2.pdfhttps://arxiv.org/pdf/2106.01483v2.pdf 代码链接&#xff1a;GitHub - Mazin-Hnewa/MS-DAYOLO: Multiscale Domain Adaptive YOLO for Cross-Domain Object DetectionMultiscale Domain Adaptive YOLO for Cross…

[JS设计模式]Command Pattern

文章目录 举例说明优点缺点完整代码 With the Command Pattern, we can decouple objects that execute a certain task from the object that calls the method. 使用命令模式&#xff0c;我们可以将执行特定任务的对象与调用该方法的对象解耦。 怎么理解 执行特定任务的对…

【SpringBoot】之Security进阶使用

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是君易--鑨&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的博客专栏《SpringBoot开发之Security系列》。&#x1f3af…

解决 Linux git push 贡献者不同(没有出现绿点)的问题

第一步&#xff0c;通过下面的指令&#xff0c;修改 linux git 的配置文件&#xff1a; vi ~/.gitconfig会进入下图界面&#xff1a; 进入本地&#xff08;Windows&#xff09;中 git 的设置界面 复制 名称 和 Email 到 gitconfig 里&#xff0c;不要在末尾加 &#xff08;空…

直排轮滑教程4

蹬地 1&#xff0c;前面练习了蹬地的结构&#xff0c;知道蹬地方向&#xff0c;如何用力。下面来练习具体的蹬地的方法&#xff0c;轮滑蹬地有自己特点。 2&#xff0c;技术方法和特点&#xff1a;蹬地速度快&#xff0c;蹬地有弹性。似跳非跳蹬。 3&#xff0c;四轮着地。轮…