在freeRTOS中,多个任务访问一块共享资源,会产生竞争现象。
比如马路上只有一个很早以前的电话亭,A、B都想要打电话,然后他们就开始打架了。但是如果A先进去了然后把门锁了,B想进去打电话的话只能在外面等,必须等到A把门锁打开。
像freeRTOS的多任务系统,任务A正在使用某个资源,还没用完的时候,任务B也来使用,就可能会导致问题。
就比如串口,任务A正在用串口发送数据,此时任务B也来用这个串口发送数据,这样就会导致数据混乱。
简而言之,多任务使用共享资源的情况下,就需要使用互斥量了。
这里有个特别强调的注意点:按照正常的情况来说,只要任务A获取了互斥量,其他任务都无法释放互斥量才对。但是freeRTOS中并没有实现,其他任务也可释放互斥量。所以在freeRTOS中,大家默认谁获取互斥量,就该谁释放。
/* 返回值:正确返回SemaphoreHandle_t 变量,错误返回NULL */ SemaphoreHandle_t xSemaphoreCreateMutex( void );
/* 参数: xSemaphore : 创建的互斥量 xBlockTime : 获取锁等待时间,超时获取失败 返回值: 获取到,返回pdTRUE,反之,pdFALSE */ SemaphoreHandle_t xSemaphoreTake( xSemaphore, xBlockTime )
/* 参数: xSemaphore : 创建的互斥量 返回值: 释放成功,返回pdTRUE,反之,pdFALSE */ SemaphoreHandle_t xSemaphoreGive( xSemaphore, xBlockTime )
if(xSemaphoreTake(xSemaphore,pdMS_TO_TICKS(1000))==pdTRUE){ /*资源处理代码,这里可以称之为临界区*/ xSemaphoreGive(xSemaphore); }
void taskA(void* pvPram) { while(1){ for(int i=0;i<5;i++){ num++; ESP_LOGI("taskA","num is %d",num); } vTaskDelay(pdMS_TO_TICKS(1000)); } } void taskB(void* pvPram) { while(1){ num++; ESP_LOGI("taskB","num is %d",num); vTaskDelay(pdMS_TO_TICKS(1000)); } } void app_main(void) { xSemaphore=xSemaphoreCreateMutex(); xTaskCreatePinnedToCore(taskA,"taskA",2048,NULL,4,NULL,1); xTaskCreatePinnedToCore(taskB,"taskB",2048,NULL,4,NULL,1); }
打印结果如下:代码目的是taskA执行过程中,num值是连续增加的,然而打印中会发现,taskA打印并不连续,会被taskB抢占。
/** * Copyright (C) 2024-2034 HalfMoon2. * All rights reserved. * * @file Filename without the absolute path * @brief Brief description * @author HalfMoon2 * @date 2025-05-07 * @version v0.1 * * @revision history: * 2025-05-07 - Initial version. */ #include <stdio.h> #include <freertos/FreeRTOS.h> #include <freertos/task.h> #include <freertos/semphr.h> #include <esp_log.h> SemaphoreHandle_t xSemaphore; BaseType_t num=0; void taskA(void* pvPram) { while(1){ /*如果在1S获取到互斥量,那么执行num++五次并打印 */ if(xSemaphoreTake(xSemaphore,pdMS_TO_TICKS(1000))==pdTRUE){ for(int i=0;i<5;i++){ num++; ESP_LOGI("taskA","num is %d",num); } xSemaphoreGive(xSemaphore); } vTaskDelay(pdMS_TO_TICKS(1000)); } } void taskB(void* pvPram) { while(1){ if(xSemaphoreTake(xSemaphore,pdMS_TO_TICKS(1000))==pdTRUE){ num++; ESP_LOGI("taskB","num is %d",num); xSemaphoreGive(xSemaphore); } vTaskDelay(pdMS_TO_TICKS(1000)); } } void app_main(void) { xSemaphore=xSemaphoreCreateMutex(); /*为了产生竞争,两个任务优先级设置一样 */ xTaskCreatePinnedToCore(taskA,"taskA",2048,NULL,4,NULL,1); xTaskCreatePinnedToCore(taskB,"taskB",2048,NULL,4,NULL,1); }
打印结果:
分析:
从打印看,当任务A执行完五次num++后,才会轮到任务B执行一次。完全符合执行逻辑。
假设有两个互斥量M1、M2,taskA获取了M1,taskB获取了M2。然而taskA需要再获取M2才能执行下一步,且taskB也需要再获取M1才能执行下一步。这样就导致了taskA、taskB都无法释放互斥量。导致了死锁。
再假设有一个互斥量M1,任务A获取了M1后,执行一个函数,然而此函数中也获取了M1,此时任务A还没释放M1,这将导致任务A自己把自己锁了。
然而递归锁,可以多次获取,可以解决以上问题
/*创建递归锁*/ SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void ); /*获得 */ BaseType_t xSemaphoreTakeRecursive( SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait); /*释放 */ BaseType_t xSemaphoreGiveRecursive( SemaphoreHandle_t xSemaphore );
SemaphoreHandle_t xSemaphore; BaseType_t num=0; void func(BaseType_t *a) { //获取互斥量等待时间设置为portMAX_DELAY,因为taskA为释放互斥量,导致此处一直等待 if(xSemaphoreTakeRecursive(xSemaphore,portMAX_DELAY)==pdTRUE){ *a=*a+1; xSemaphoreGiveRecursive(xSemaphore); } } void taskA(void* pvPram) { while(1){ /*如果在1S获取到互斥量,那么执行num++五次并打印 */ if(xSemaphoreTake(xSemaphore,pdMS_TO_TICKS(1000))==pdTRUE){ func(&num); ESP_LOGI("taskA","num is %d",num); xSemaphoreGive(xSemaphore); } vTaskDelay(pdMS_TO_TICKS(1000)); } } void app_main(void) { xSemaphore=xSemaphoreCreateMutex(); /*为了产生竞争,两个任务优先级设置一样 */ xTaskCreatePinnedToCore(taskA,"taskA",2048,NULL,4,NULL,1); }
打印如下:程序将一直锁着,无法执行
SemaphoreHandle_t xSemaphore; BaseType_t num=0; void func(BaseType_t *a) { //获取互斥量等待时间设置为portMAX_DELAY,因为taskA为释放互斥量,导致此处一直等待 if(xSemaphoreTakeRecursive(xSemaphore,portMAX_DELAY)==pdTRUE){ *a=*a+1; xSemaphoreGiveRecursive(xSemaphore); } } void taskA(void* pvPram) { while(1){ /*如果在1S获取到互斥量,那么执行num++五次并打印 */ if(xSemaphoreTakeRecursive(xSemaphore,pdMS_TO_TICKS(1000))==pdTRUE){ func(&num); ESP_LOGI("taskA","num is %d",num); xSemaphoreGiveRecursive(xSemaphore); } vTaskDelay(pdMS_TO_TICKS(1000)); } } void app_main(void) { xSemaphore=xSemaphoreCreateRecursiveMutex(); /*为了产生竞争,两个任务优先级设置一样 */ xTaskCreatePinnedToCore(taskA,"taskA",2048,NULL,4,NULL,1); }
打印如下:发现完全可以正常打印了
作者: 二月半, 来源:面包板社区
链接: https://mbb.eet-china.com/blog/uid-me-1862109.html
版权声明:本文为博主原创,未经本人允许,禁止转载!
文章评论(0条评论)
登录后参与讨论