如何检查FreeRTOS任务是否已删除?遗留代码场景方案探讨
注:这是一种糟糕的设计模式,不应创建临时FreeRTOS任务。 我在撰写的FreeRTOS最佳实践指南(第17条)中已提及这一点:
[安全关键] 使用软件定时器和回调,而非超时后执行操作并自行删除的短期(临时)任务。
……但我受困于遗留代码库,只能逐步修复,因此目前……
检查FreeRTOS任务是否已删除的最佳方式(或多种有效方式)是什么?目前我通过跟踪任务句柄,在调用vTaskDelete()前将其设为NULL,代码如下:
static TaskHandle_t myTaskHandle = NULL; void myTask(void* params) { for (;;) { // 执行操作 // - 在特定条件下,代码可能退出此循环以终止任务 } // 手动将句柄设为NULL以跟踪任务已删除状态 myTaskHandle = NULL; vTaskDelete(NULL); // 删除自身 } // 此函数可被多次调用,需确保任务未运行时才重新创建 void myTaskInit() { // 检查任务是否已在运行,若已运行则不再创建 if (myTaskHandle != NULL) { printf("Error: myTask task already running"); return; } static StackType_t taskStack[configMINIMAL_STACK_SIZE]; static StaticTask_t taskControlBlock; myTaskHandle = xTaskCreateStatic( myTask, "My Task", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, taskStack, &taskControlBlock); }
是否有更好的方式检查任务是否已删除?目前我使用if (myTaskHandle != NULL)进行判断。
此外,此处存在竞态条件:
myTaskHandle = NULL; vTaskDelete(NULL);
myTask在调用myTaskHandle = NULL;后、vTaskDelete(NULL);前可能被其他任务抢占。若此时其他任务调用myTaskInit(),会认为任务已删除(句柄为NULL),从而在即将被删除的任务之上重新创建任务,导致未定义行为。
因此我考虑添加如下保护:
taskENTER_CRITICAL(); // 禁用FreeRTOS上下文切换 myTaskHandle = NULL; vTaskDelete(NULL); taskEXIT_CRITICAL(); // 重新启用FreeRTOS上下文切换
……但我认为这行不通,因为调用vTaskDelete(NULL);后,taskEXIT_CRITICAL();永远不会被执行,对吗?或者我错了?
更新:我是对的。vTaskDelete()函数末尾会触发任务调度,如下代码所示(调用portYIELD_WITHIN_API(),会触发上下文切换):
/* 若刚被删除的是当前运行任务,则强制重新调度 */ if( xSchedulerRunning != pdFALSE ) { if( pxTCB == pxCurrentTCB ) { configASSERT( uxSchedulerSuspended == 0 ); portYIELD_WITHIN_API(); // <===== 此处 ===== } else { mtCOVERAGE_TEST_MARKER(); } }
另外,我认为eTaskGetState()无法判断任务是否已删除。虽然其返回值包含Deleted状态,但这仅表示任务控制块(TCB)等待空闲任务清理,清理完成后任务将不再有任何状态。
状态 返回值 ... Deleted eDeleted(任务TCB等待清理)
参考资料
- xTaskCreateStatic 官方文档
注:我也在FreeRTOS论坛上发布了此问题。
内容的提问来源于stack exchange,提问作者Gabriel Staples

