4. 使用任务句柄控制任务
任务目标
创建两个RTOS任务,通过任务句柄实现对任务的挂起(Suspend)、恢复(Resume)和删除(Delete)操作,掌握任务状态控制的核心API用法。
核心知识点
- 任务句柄是RTOS中唯一标识任务的变量,类型为
TaskHandle_t,在创建任务时通过指针参数返回。 - 任务状态机:就绪(Ready)、运行(Running)、阻塞(Blocked)、挂起(Suspended)。挂起与阻塞的区别在于:挂起任务不参与调度,只能通过
vTaskResume()恢复;阻塞任务在超时或事件满足后自动进入就绪态。 - 删除任务后,其句柄变为无效,不能再对该句柄执行挂起/恢复操作。
完整代码(FreeRTOS)
#include "FreeRTOS.h"
#include "task.h"
#include "stdio.h"
// 任务句柄定义
TaskHandle_t xTask1Handle = NULL;
TaskHandle_t xTask2Handle = NULL;
// 任务1:控制任务,负责挂起/恢复/删除任务2
void vControlTask(void *pvParameters)
{
const TickType_t xDelay = pdMS_TO_TICKS(1000);
uint8_t ucStep = 0;
for (;;)
{
switch (ucStep)
{
case 0:
printf("[Ctrl] Suspending Task2...\r\n");
vTaskSuspend(xTask2Handle); // 挂起任务2
break;
case 1:
printf("[Ctrl] Resuming Task2...\r\n");
vTaskResume(xTask2Handle); // 恢复任务2
break;
case 2:
printf("[Ctrl] Deleting Task2...\r\n");
vTaskDelete(xTask2Handle); // 删除任务2
xTask2Handle = NULL; // 句柄置空,防止悬空指针
break;
default:
printf("[Ctrl] Control task complete.\r\n");
vTaskDelete(NULL); // 删除自身
break;
}
ucStep++;
vTaskDelay(xDelay);
}
}
// 任务2:被控制任务,周期性打印运行状态
void vWorkTask(void *pvParameters)
{
const TickType_t xDelay = pdMS_TO_TICKS(500);
uint32_t ulCount = 0;
for (;;)
{
printf("[Work] Running, count=%lu\r\n", ulCount++);
vTaskDelay(xDelay);
}
}
int main(void)
{
// 创建两个任务,任务句柄通过最后一个参数返回
xTaskCreate(vControlTask, "Ctrl", 128, NULL, 1, &xTask1Handle);
xTaskCreate(vWorkTask, "Work", 128, NULL, 2, &xTask2Handle);
// 启动调度器
vTaskStartScheduler();
// 正常不会执行到这里
for (;;);
}
运行与验证
- 环境准备:使用FreeRTOS官方演示工程(如QEMU模拟器或STM32开发板),将上述代码替换
main.c中的默认任务创建部分,确保printf已重定向到串口或控制台。 - 编译与烧录:编译工程并下载到目标平台,打开串口终端(波特率115200)。
- 预期输出时序(每行间隔约1秒):
[Work] Running, count=0 [Work] Running, count=1 [Ctrl] Suspending Task2... [Ctrl] Resuming Task2... [Work] Running, count=2 [Work] Running, count=3 [Ctrl] Deleting Task2... [Ctrl] Control task complete. - 判定通过条件:
- 挂起后,任务2的
[Work]打印停止,说明任务不再被调度; - 恢复后,任务2从
count=2继续打印(而非从0开始),说明任务未被删除,仅暂停执行; - 删除后,任务2的打印不再出现,且控制任务打印完成并自我删除,系统无崩溃或异常输出。
- 挂起后,任务2的
关键说明
vTaskSuspend()挂起任务时,若传入NULL表示挂起当前运行任务;vTaskResume()不能用于恢复因vTaskDelay()或事件等待而阻塞的任务,只能恢复被vTaskSuspend()挂起的任务。- 删除任务后必须将句柄置为
NULL,避免后续误用悬空指针;被删除任务的所有资源(栈、TCB)由空闲任务回收。 - 本示例中控制任务优先级(1)低于工作任务(2),因此挂起/恢复操作发生在工作任务阻塞期间,保证输出时序清晰可预测。