FreeRTOS:删除任务的指南

FreeRTOS 删除任务

概述

任务的删除使用的 API 为:

void vTaskDelete( TaskHandle_t xTask );

任务删除主要是两种情况:

  1. 自删除,即在任务本身的 TaskCode 中调用 vTaskDelete(null)删除自身。

  2. 强制删除,即在其他任务中,删除另一个任务。

FreeRTOS 创建任务的 Static 版本 一节中介绍了创建任务需要的两块存储空间,根据创建函数时使用的 API,在调用 vTaskDelete() 后,对应存储资源被释放的时机有以下区别:
1)使用 xTaskCreate()创建的任务,调用 vTaskDelete() 后,对应的存储资源将在 idle task(freertos 中系统自带的一个 task,我们将在后续小节中介绍它) 中被自动释放。因此,使用xTaskCreate()创建的任务,删除任务后若要立即分配资源,应当稍作延时,给予 idle task 一些回收资源的时间。否则可能不能更好的分配资源,也可能加重资源碎片的风险。
2)使用 xTaskCreateStatic()静态创建的任务,存储资源并不会被释放,需要手动释放。

需求及功能解析

本小节主要介绍删除任务的方法:

1)自删除:

static void task2_process(void *arg)
{
    static const char *TASK2_TAG = "TASK2";
    while (1) {
        ESP_LOGI(TASK2_TAG, "task2_flag = %d, arg2 = %s", task2_flag, (char *)arg);
        task2_flag++;
        if(task2_flag > 5) {
            break;
        }
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
    vTaskDelete(NULL);
}

2)强制删除:

 // Use the handle to delete the task.
    if(task3 != NULL) {
        vTaskDelete(task3);
    }

删除任务时的常规考虑因素

删除任务实际要考虑的事情很多,因为任务删除时可能出现的后遗症问题:

  1. 占用共享资源未被释放,可能影响其他任务的运行
  2. 通信关系的上家(即发送消息或者数据的任务或 ISR)没有处理到位,导致上家的消息或者数据累积
  3. 通信关系的下家(即接受消息或者数据的任务)得不到消息或者数据,导致下家无法正常运行
  4. 申请的资源未释放、赋值的变量未复位
    因此,如果该任务占用了共享资源、申请了新资源,则删除前必须复位、释放资源。
  5. 该任务有关联的任务或ISR,删除前请考虑对他们的影响.
  6. 在强制删除一个任务的时候,可以先获取下 task 的state,若该任务正在挂起或者延时,则可以尝试删除之。
    总之,删除任务一定要本着“干干净净的来,干干净净的走“,推荐使用自删除的方式。

双核 ESP32 删除任务时的注意事项

删除固定到另一个 CPU 内核的任务时,该任务的内存总是由另一个内核的空闲任务(即 idle task)释放(因为需要清除FPU寄存器)。
删除当前在另一个核心上运行的任务时,会在另一个核心上触发让步(yield),任务的内存会被其中一个空闲任务释放(取决于任务的核心关联性)。

应避免对当前正在另一个内核上运行的任务调用vTaskDelete()。这是因为很难知道另一个内核上当前运行的任务正在执行什么,因此可能会导致不可预测的行为,例如:

1)删除包含互斥对象的任务
2)删除尚未释放之前分配的内存的任务

在可能的情况下,应该设计他们的应用程序,使vTaskDelete()只在已知状态下的任务上被调用。例如:

1)任务执行完成后会自动删除(通过vTaskDelete(NULL)),并已清理任务中使用的所有资源。
2)任务在被另一个任务删除之前(通过vTaskSuspend())将自身置于挂起状态。

这里介绍了很多,对初学者不必关心所有的方面,在开发中遇到这些问题时可以再看一下这些总结。

示例解析

示例输出:

this is esp32 chip with 2 CPU core(s), WiFi/BT/BLE, Minimum free heap size: 294424 bytes
I (336) TASK2: task2_flag = 0, arg2 = 2
I (336) TASK3: task3_flag = 0, arg3 = 3
I (1336) TASK2: task2_flag = 1, arg2 = 2
I (2336) TASK2: task2_flag = 2, arg2 = 2
I (3336) TASK2: task2_flag = 3, arg2 = 2
I (4336) TASK2: task2_flag = 4, arg2 = 2
I (5336) TASK2: task2_flag = 5, arg2 = 2

如上所述,无论是自删除还是强制删除,任务删除后,将停止运行。

讨论-删除任务与挂起任务的异同

上一节介绍了任务的挂起与恢复。这里删除任务、挂起任务做一些对比:

相同点:

1)删除任务、挂起任务后,任务都将停止执行。

不同点:

1)删除任务后,任务的资源就被释放了,包括任务的状态、局部变量等信息;挂起任务则会保留任务的资源以及与任务相关的信息。

2)删除任务后,若想让任务继续执行,需要重新创建任务,且重新创建的任务会重新从 TaskCode 的第一行代码执行。挂起的任务只需要重新恢复执行就可以重新执行,且从 TaskCode 的暂停处的代码继续执行。另外,重新创建任务相比恢复任务,前者更加消耗时间。

总结

1)任务删除主要是两种情况:自删除、强制删除。

2)删除任务后若要立即分配资源,应当稍作延时,给予 idle task 一些回收资源的时间。否则可能不能更好的分配资源,也可能加重资源碎片的风险。

3)删除任务需要考虑资源回收、共享资源、消息通信相关的处理问题。

4)ESP32 是双核系统,应避免对当前正在另一个内核上运行的任务调用vTaskDelete()。

资源链接

1)Learning-FreeRTOS-with-esp32 系列博客介绍
2)对应示例的 code 链接 (点击直达代码仓库)

3)下一篇:浅析 FreeRTOS SysTick 和任务延时

物联沃分享整理
物联沃-IOTWORD物联网 » FreeRTOS:删除任务的指南

发表评论