4. 任务删除与自我删除

任务目标

本案例演示FreeRTOS中任务删除的两种典型用法:任务自我删除(任务运行完毕后调用vTaskDelete(NULL)回收自身)与任务删除其他任务(通过任务句柄删除指定任务)。最终通过LED闪烁与串口日志验证删除行为是否符合预期。

核心知识点

  • vTaskDelete(TaskHandle_t pxTask):删除参数指定的任务;传入NULL时删除当前运行任务(自我删除)。
  • 被删除任务进入就绪态后,其TCB与栈空间由空闲任务负责释放,因此必须确保空闲任务不被阻塞或挂起。
  • 任务删除后,其句柄不再有效,不应再次调用vTaskDelete或vTaskSuspend等API操作该句柄。

完整代码

#include "FreeRTOS.h"
#include "task.h"
#include "led.h"      // 板级LED驱动,提供 LED_Init/LED_On/LED_Off
#include "debug.h"    // 板级串口驱动,提供 DEBUG_Init/DEBUG_Printf

/* 任务句柄 */
static TaskHandle_t xTaskDeleteeHandle = NULL;   // 被删除任务句柄
static TaskHandle_t xTaskDeleterHandle = NULL;   // 执行删除操作的任务句柄

/* 任务1:运行5次后自我删除 */
static void vTaskSelfDelete(void *pvParameters)
{
    uint8_t ucCount = 0;
    const TickType_t xDelay = pdMS_TO_TICKS(200);

    for (;;)
    {
        ucCount++;
        LED_On();
        vTaskDelay(xDelay);
        LED_Off();
        vTaskDelay(xDelay);

        DEBUG_Printf("Self-delete task running, count = %d\r\n", ucCount);

        if (ucCount >= 5)
        {
            DEBUG_Printf("Self-delete task will delete itself now.\r\n");
            vTaskDelete(NULL);   // 自我删除
        }
    }
}

/* 任务2:延时后删除任务1 */
static void vTaskDeleteOther(void *pvParameters)
{
    const TickType_t xDelay500ms = pdMS_TO_TICKS(500);
    const TickType_t xDelay2s = pdMS_TO_TICKS(2000);

    vTaskDelay(xDelay2s);   // 等待任务1先运行一段时间

    if (xTaskDeleteeHandle != NULL)
    {
        DEBUG_Printf("Deleter task is deleting the self-delete task.\r\n");
        vTaskDelete(xTaskDeleteeHandle);   // 删除任务1
        xTaskDeleteeHandle = NULL;         // 句柄置空,防止悬垂指针
    }

    /* 再延时后自我删除,结束本任务 */
    vTaskDelay(xDelay500ms);
    DEBUG_Printf("Deleter task finishes its job, delete itself.\r\n");
    vTaskDelete(NULL);
}

int main(void)
{
    LED_Init();
    DEBUG_Init();

    /* 创建任务1:自我删除 */
    xTaskCreate(vTaskSelfDelete, "SelfDel", 128, NULL, 2, &xTaskDeleteeHandle);

    /* 创建任务2:删除其他任务 */
    xTaskCreate(vTaskDeleteOther, "Deleter", 128, NULL, 1, &xTaskDeleterHandle);

    vTaskStartScheduler();

    /* 正常不会运行到这里 */
    for (;;);
}

运行与验证

  1. 编译与烧录:将代码加入FreeRTOS工程(需包含led.c/h与debug.c/h板级驱动),编译后烧录至开发板。
  2. 预期行为:
    • 任务1(优先级2)每200ms翻转一次LED,并打印运行次数;运行5次后打印删除信息并自我删除。
    • 任务2(优先级1)启动后延时2秒,此时任务1已运行约5次(每次400ms周期),任务2随即删除任务1,打印删除信息。
    • 任务2再延时500ms后自我删除。此后系统中仅剩空闲任务,LED停止翻转,串口不再输出。
  3. 判定通过条件:
    • 串口依次输出任务1的5次计数(count=1~5)、任务1自我删除信息、任务2删除任务1的信息、任务2自我删除信息。
    • LED在任务1运行期间持续闪烁,任务1被删除后LED停止变化。
    • 系统不崩溃、不死机,空闲任务正常运行。

关键说明

  • 任务1的xTaskDeleteeHandle在创建时被赋值,任务2通过该句柄删除任务1。删除后立即将句柄置NULL,避免后续误用。
  • 自我删除使用vTaskDelete(NULL),当前任务停止运行并进入待回收状态,内存由空闲任务释放。
  • 若任务2先于任务1完成删除(例如调整延时),需保证任务1尚未自我删除,否则句柄已失效。本案例中任务1运行5次约需2秒(5×400ms),任务2延时2秒后删除,时序上任务1恰好刚完成第5次自我删除,此时任务2再调用vTaskDelete操作已删除任务的句柄属于未定义行为。因此实际测试时建议将任务2延时调整为1.5秒(xDelay2s = pdMS_TO_TICKS(1500)),确保任务1仍在运行状态时被任务2删除,从而清晰展示“删除其他任务”的效果。
0s 0.5s 1.0s 1.5s 2.0s 任务1 (SelfDel, 优先级2) 自我删除 count=1 2 3 4 5 任务2 (Deleter, 优先级1) 延时等待 (2s) vTaskDelete(句柄) 延时0.5s后自删 任务1运行 任务2运行 延时等待 ✕ 删除点