如何在 freeRTOS 中使用互斥锁同步线程?

How to sync thread using mutex in freeRTOS?

我有一个用 freeRTOS 写的代码,我必须实现互斥锁同步,这样就不会发生并发,但我一直面临一个问题:

xSemaphoreHandle xMutex=NULL;

void fn_test1()
{
    int i=0;
    while(1)
    {

        xSemaphoreTake(xMutex,portMAX_DELAY );
        ESP_LOGI(TAG, "Task1 output = %d", i++);
        xSemaphoreGive(xMutex);
        vTaskDelay(10);
    }
}   
void fn_test2()
{
    int i=0;
    while(1)
    {
        xSemaphoreTake(xMutex,portMAX_DELAY );
        ESP_LOGI(TAG, "Task2 output = %d", i++);
        xSemaphoreGive(xMutex);
        vTaskDelay(10);
    }
}   

void app_main()
{
    char task_name[16];
    ESP_ERROR_CHECK( nvs_flash_init() );
    // initialise_wifi();
    // wait_for_ip();
    xMutex=xSemaphoreCreateMutex();  //xMutex will be storing NULL when Mutex not created
    if(xMutex!=NULL) {
        xTaskCreate(fn_test1,"task1",4096,NULL,1,NULL);
        xTaskCreate(fn_test2,"task2",4096,NULL,1,NULL);
        //vTaskStartScheduler();
    }
}

代码的问题是我正在获取输出

[0;32mI (40) example: Task1 output = 0[0m
[0;32mI (50) example: Task2 output = 0[0m
[0;32mI (547) example: Task2 output = 1[0m
[0;32mI (547) example: Task1 output = 1[0m
[0;32mI (647) example: Task2 output = 2[0m
[0;32mI (647) example: Task1 output = 2[0m

-->

[0;32mI (747) example: Task1 output = 3[0m
[0;32mI (747) example: Task2 output = 3[0m
[0;32mI (847) example: Task2 output = 4[0m
[0;32mI (847) example: Task1 output = 4[0m
[0;32mI (947) example: Task1 output = 5[0m

<--

如你所见,task1 执行了两次,然后 task2 执行了两次,这不是我期望的理想输出,理想的输出应该是 task1 和 task2 之间交替。

我发现一篇文章实现了这种行为,link,但它建议修改 freeRTOS 内核。 我寻求替代解决方案。感谢任何帮助。

感谢和问候

如果省略延迟(在释放互斥量之后)会怎么样?我想您会看到所需的切换行为。 我可以想象,例如task1 在释放互斥量后仍在休眠 task2 足够快地完成输出(在 1 个滴答周期内)并且也睡着了。因此,两个任务的延迟同时到期或滴答作响。鉴于两个任务具有相同的优先级,调度程序完全可以自由地执行任一任务 运行。

由于这两个任务具有相同的优先级,因此它们相互等待一个 systick 周期是可以接受的行为(因为它们只会在 systick 中被安排!) - 这不是一个错误,而是一个功能!

获取互斥量,执行操作(示例代码中的输出调用)并返回互斥量显然比系统周期要少得多(至少,你现在(零?)由其他人加载 tasks/ISRs.

因此,我们应该考虑为什么您希望它们完全交替。 如果您只想安排对共享资源的访问周期 "equally",可能值得研究一下如果它们在某个较长的周期(即五十个)内具有相同数量的互斥锁分配是否也足够-50 次分享,但不是交替分配)。相反,如果 要求为什么必须在任务 2 之后(和之前)处理任务 2(出于您的应用程序的某些原因),那么实施 "token-like" 设计,其中每个任务显式触发另一个任务(例如,每个方向使用一个事件,但省略当前互斥锁)。如果您围绕这对任务创建一些额外的系统负载,此设计也会表现稳定 - 而当前的互斥锁+延迟设计将需要在每次围绕您的两个任务的负载情况发生变化时重新调整。