You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FreeRTOS调用xSemaphoreTake后无限阻塞且无上下文切换问题

问题根源与解决方案

核心问题1:全局对象初始化时机早于FreeRTOS调度器启动

你的os2::thread2 th{};是全局对象,它的构造函数会在FreeRTOS调度器启动前执行(也就是app_main运行之前,甚至早于系统初始化FreeRTOS内核)。

在FreeRTOS规则中:

  • 调用xTaskCreateStaticPinnedToCore创建任务后,任务仅会被加入就绪队列,但调度器未启动时,任务永远不会被调度执行。
  • xSemaphoreTake(..., portMAX_DELAY)在调度器未启动时,不会触发任务切换——因为FreeRTOS的任务切换依赖调度器的时钟节拍或主动触发的调度逻辑,此时内核还未运行,调用该函数会直接进入死循环,永远等不到信号量。

这就是程序仅输出init! Hello World!后停滞的原因:构造函数卡在xSemaphoreTake,worker任务根本没机会运行。

核心问题2:局部变量生命周期风险(次要但必须修复)

你的comm_obj comm是thread2构造函数里的局部变量,即使调度器能正常启动,当构造函数执行完毕退出后,comm会被销毁,worker任务里通过param访问的comm.sem会变成野指针,导致未定义行为。

修复步骤

步骤1:将对象初始化移到FreeRTOS调度器启动后

把thread2的创建从全局变量移到app_main里,确保在FreeRTOS内核运行后再执行任务创建和同步逻辑:

修改main.cpp:

#include "thread2.hpp"

extern "C" auto app_main() -> void {
    os2::thread2 th{}; // 此时调度器已启动
    vTaskDelay(portMAX_DELAY);
};

步骤2:修正comm_obj的生命周期

把comm_obj改为thread2的成员变量,避免局部变量销毁导致的野指针:

修改thread2.hpp:

#include <cstdio>
#include <array>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"

namespace os2
{
    struct comm_obj
    {
        SemaphoreHandle_t sem;
        StaticSemaphore_t sem_buffer; // 把静态信号量缓冲区也放到成员中
    };

    inline auto worker(void* const param) -> void
    {
        printf("Worker task before giving semaphore\n");
        fflush(stdout);

        auto& comm = *static_cast<comm_obj*>(param);
        xSemaphoreGive(comm.sem);

        printf("Worker after giving semaphore!\n");
        fflush(stdout);

        vTaskDelete(NULL);
    }

    struct thread2
    {
        thread2() {
            printf("init!\n");
            // 初始化成员变量里的信号量
            comm_.sem = xSemaphoreCreateBinaryStatic(&comm_.sem_buffer);

            printf("Hello ");
            fflush(stdout);
            [[maybe_unused]] TaskHandle_t handle = xTaskCreateStaticPinnedToCore(
                &worker,
                "default",
                5*1024,
                &comm_, // 传递成员变量的地址
                10,
                stack_.data(),
                &tcb_,
                0);

            printf("World!\n");
            fflush(stdout);
            xSemaphoreTake(comm_.sem, portMAX_DELAY);
        }

        StaticTask_t tcb_;
        std::array<StackType_t, 5*1024> stack_;
        comm_obj comm_; // 作为成员变量保存,避免生命周期问题
    };
}

验证修复效果

修复后程序会输出:

init!
Hello World!
Worker task before giving semaphore
Worker after giving semaphore!

之后app_main进入延迟状态,不会触发任务看门狗。

内容的提问来源于stack exchange,提问作者glades

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 08:15:05