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 (;;);
}

运行与验证

  1. 环境准备:使用FreeRTOS官方演示工程(如QEMU模拟器或STM32开发板),将上述代码替换main.c中的默认任务创建部分,确保printf已重定向到串口或控制台。
  2. 编译与烧录:编译工程并下载到目标平台,打开串口终端(波特率115200)。
  3. 预期输出时序(每行间隔约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.
    
  4. 判定通过条件:
    • 挂起后,任务2的[Work]打印停止,说明任务不再被调度;
    • 恢复后,任务2从count=2继续打印(而非从0开始),说明任务未被删除,仅暂停执行;
    • 删除后,任务2的打印不再出现,且控制任务打印完成并自我删除,系统无崩溃或异常输出。

关键说明

  • vTaskSuspend()挂起任务时,若传入NULL表示挂起当前运行任务;vTaskResume()不能用于恢复因vTaskDelay()或事件等待而阻塞的任务,只能恢复被vTaskSuspend()挂起的任务。
  • 删除任务后必须将句柄置为NULL,避免后续误用悬空指针;被删除任务的所有资源(栈、TCB)由空闲任务回收。
  • 本示例中控制任务优先级(1)低于工作任务(2),因此挂起/恢复操作发生在工作任务阻塞期间,保证输出时序清晰可预测。