> 文章列表 > 【FreeRTOS学习 - 互斥量学习 (盗锁)】

【FreeRTOS学习 - 互斥量学习 (盗锁)】

【FreeRTOS学习 - 互斥量学习 (盗锁)】

跟着韦东山老师FreeRTOS教学资料的学习记录

FreeRTOS全部项目代码链接(更新中)

https://gitee.com/chenshao777/free-rtos_-study


了解互斥量的使用场景

例如多个任务对同一个数组或者变量进行操作时,往往会发生冲突,可能任务A访问arr数组的过程中被任务B打断,任务B恰好也对arr数组进行了操作,当任务A继续执行时,此时的arr数组已经不是当时的arr数组了,这就造成了访问冲突。

即非原子化访问,也就是操作的过程可能被打断

为了避免此问题,可以在访问变量前,上锁,访问之后,开锁

  • 即A使用变量前上个锁
  • A使用完再开锁
  • 再此期间B必须等A开锁后才能使用该变量,这样就不会产生访问冲突了
  • 但实际上FreeRTOS的代码并没有实现谁上的锁只能由谁来开
  • 所以A上锁后,B实际上也是可以自己开锁的
  • 所以谁上锁,谁才能开锁需要由程序员自己约定了
  • 这个锁就是我们说的互斥量,互斥量是一种特殊的二进制信号量

要想使用互斥量,需要在配置文件FreeRTOSConfig.h中定义:

#define   configUSE_MUTEXES   1

一、创建互斥量

二、互斥量的操作函数(获取、释放、删除)

三、互斥量使用实例

四、盗锁


一、创建互斥量

QueueHandle_t xSemaphoreMutex;  //互斥量句柄/* 创建互斥量,返回它的句柄 */
xSemaphoreMutex = xSemaphoreCreateMutex();

二、互斥量的操作函数(获取、释放、删除)

/** xSemaphore: 信号量句柄,你要删除哪个信号量, 互斥量也是一种信号量*/
void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );/* 释放 */
BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );/* 释放(ISR版本) */
BaseType_t xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore,BaseType_t *pxHigherPriorityTaskWoken);/* 获取 */
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore,TickType_t xTicksToWait);
/* 获取 (ISR版本) */
xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore,BaseType_t *pxHigherPriorityTaskWoken);

三、互斥量使用实例

创建两个任务

  • 任务1一直发送0~99数字
  • 任务2一直发送100~199数字

1. 不使用互斥量的情况(USE_Mutex_Flag 置0)

实验结果,发送数据混乱在一起:

在这里插入图片描述
2. 使用互斥量的情况(USE_Mutex_Flag 置1)

实验结果,发送数据不产生错乱了:

在这里插入图片描述
代码如下:

void systemInit(void);   //各模块初始化QueueHandle_t xSemaphoreMutex;  //互斥量句柄void systemInit(void);   //各模块初始化QueueHandle_t xSemaphoreMutex;  //互斥量句柄/* 使用互斥量标志 */
#define     USE_Mutex_Flag 	   0   /** 任务1一直发送0~99数字
*/
void vSemphrMutex_Task1(void *pvParameters)
{const TickType_t xDelay = 50 / portTICK_PERIOD_MS;BaseType_t result;for(;;){#if (USE_Mutex_Flag == 1)/* 上锁 */xSemaphoreTake(xSemaphoreMutex, portMAX_DELAY);#endif/* 打印 0 ~ 99 */for(int i=0; i < 100 ; i++){printf("%d ", i);}printf("\\r\\n");#if (USE_Mutex_Flag == 1)/* 开锁 */xSemaphoreGive(xSemaphoreMutex);#endifvTaskDelay(xDelay);}
}/** 任务2一直发送100~199数字
*/
void vSemphrMutex_Task2(void *pvParameters)
{const TickType_t xDelay = 50 / portTICK_PERIOD_MS;BaseType_t result;  //获取信号量结果变量for(;;){#if (USE_Mutex_Flag == 1)/* 上锁 */xSemaphoreTake(xSemaphoreMutex, portMAX_DELAY);#endif/* 打印 100 ~ 199 */for(int i=100; i < 200 ; i++){printf("%d ", i);}printf("\\r\\n");#if (USE_Mutex_Flag == 1)/* 开锁 */xSemaphoreGive(xSemaphoreMutex);#endifvTaskDelay(xDelay);}
}int main()
{systemInit();   //初始化各个模块// 互斥量任务一xTaskCreate(vSemphrMutex_Task1, "vSemphrMutex_Task1", 200, NULL, 2, NULL);// 互斥量任务二xTaskCreate(vSemphrMutex_Task2, "vSemphrMutex_Task2", 200, NULL, 2, NULL);vTaskStartScheduler();          //开启任务调度
}/** 初始化各个模块
*/
void systemInit(void)
{SysTick_Init(72);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);USART1_Init(115200);printf("串口初始化成功!\\r\\n");/* 创建互斥量 */xSemaphoreMutex = xSemaphoreCreateMutex();
}

四、盗锁

  • 任务2上锁后,开始打印

  • 任务1来上锁(Take),发现失败,不等待

  • 任务1直接开锁(Give),简称盗锁(因为本来约定谁上的锁只能由谁开)

  • 任务1开锁(Give)成功,开始打印

  • 由于优先级相同,任务1和2交替打印

  1. 任务1盗锁代码如下:

在这里插入图片描述

  1. 盗锁结果:

在这里插入图片描述

空气净化器