递归锁 非递归锁

一、先上结论:

递归锁:同一个线程在不解锁的情况下,可以多次获取锁定同一个递归锁,而且不会产生死锁;

非递归锁:在不解锁的情况下,当同一个线程多次获取同一个递归锁时,会产生死锁。

二、典型应用:

windows下的互斥量和临界区(关键段)是递归锁;

linux下的互斥量pthread_mutex_t是非递归锁,但是可以通过在创建互斥量时设置PTHREAD_MUTEX_RECURSIVE属性,将pthread_mutex_t设置为递归锁。

三、举例

critical_section递归锁

#include <Windows.h>
#include <iostream>
#include <string>
​
int counter = 0;
​
CRITICAL_SECTION g_cs;
​
void doit(void* arg)
{
    int i, val;
    for (i=0; i<5000; i++)
    {
        EnterCriticalSection(&g_cs);
        EnterCriticalSection(&g_cs);
​
        val = counter;
        printf("thread %d : %d\n", int(arg), val+1);
        counter = val + 1;
​
        LeaveCriticalSection(&g_cs);
        LeaveCriticalSection(&g_cs);
    }
}
​
int main(int argc, char*argv[])
{
    InitializeCriticalSection(&g_cs);
​
    HANDLE hThread1 = CreateThread(NULL,0, (LPTHREAD_START_ROUTINE)doit, (void*)1, 0, NULL);
    HANDLE hTrehad2 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)doit, (void*)2, 0, NULL);
​
    WaitForSingleObject(hThread1, INFINITE);
    WaitForSingleObject(hTrehad2, INFINITE);
​
    DeleteCriticalSection(&g_cs);
​
    
    return 0;
}
结果:加1次锁和2次锁,均可以正确的输出1~10000。

pthread_mutex_t非递归锁

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
​
int counter = 0;
​
pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;
​
void* doit(void*)
{
        int i, val;
        for (i=0; i<5000; i++)
        {
                pthread_mutex_lock(&g_mutex);
                pthread_mutex_lock(&g_mutex);
​
                val = counter;
                printf("%x: %d\n", pthread_self(), val+1);
                counter = val + 1;
​
                pthread_mutex_unlock(&g_mutex);
                pthread_mutex_unlock(&g_mutex);      
        }
}
​
int main(int argc, char*argv[])
{
        pthread_t tid1, tid2;
​
        pthread_create(&tid1, NULL, doit, NULL);
        pthread_create(&tid2, NULL, doit, NULL);
​
        pthread_join(tid1, NULL);
        pthread_join(tid2, NULL);
​
        return 0;
}
结果:加1次锁,可以正确的输出1~10000;加2次锁,死锁,不输出任何信息。

pthread_mutex_t递归锁

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
​
int counter = 0;
​
pthread_mutex_t g_mutex;// = PTHREAD_MUTEX_INITIALIZER;
​
void* doit(void*)
{
        int i, val;
        for (i=0; i<5000; i++)
        {
                pthread_mutex_lock(&g_mutex);
                pthread_mutex_lock(&g_mutex);
​
                val = counter;
                printf("%x: %d\n", pthread_self(), val+1);
                counter = val + 1;
​
                pthread_mutex_unlock(&g_mutex);
                pthread_mutex_unlock(&g_mutex);
        }
}
​
int main(int argc, char*argv[])
{
        //create recursive attribute
        pthread_mutexattr_t attr;
        pthread_mutexattr_init(&attr);
​
        //set recursive attribute
        pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
​
        pthread_mutex_init(&g_mutex, &attr);
​
​
        pthread_t tid1, tid2;
        pthread_create(&tid1, NULL, doit, NULL);
        pthread_create(&tid2, NULL, doit, NULL);
​
        pthread_join(tid1, NULL);
        pthread_join(tid2, NULL);
​
​
        pthread_mutex_destroy(&g_mutex);
​
        //destroy recursive attribute
        pthread_mutexattr_destroy(&attr);
​
        return 0;
}
结果:加1次锁和2次锁,均可以正确的输出1~10000。
<think>嗯,用户问的是FreeRTOS中递归的使用方法和错误解决方案。首先,我需要回忆一下FreeRTOS中递归的概念。递归,也就是递归互斥量,允许同一个任务多次获取而不被阻塞,这在递归函数或者需要多次进入临界区的场景中很有用。 用户可能已经知道普通的互斥,但遇到了需要多次获取的情况,导致死,所以才会转向递归。需要解释递归的基本用法,比如创建、获取和释放的步骤。然后,要提到使用的API函数,比如xSemaphoreCreateRecursiveMutex(),以及对应的xSemaphoreTakeRecursive()和xSemaphoreGiveRecursive()。这些函数和普通互斥量的区别在于它们允许递归调用,必须成对使用。 接下来,用户可能在使用过程中遇到了常见错误,比如未使用递归函数导致死,或者释放次数不匹配造成资源泄漏。这时候需要给出具体的错误示例和解决方案。比如,如果任务没有使用TakeRecursive而用了普通的Take,就会卡住,这时候要检查函数是否正确使用递归版本。 另外,还要提到优先级反转的问题,虽然递归本身不能解决优先级反转,但FreeRTOS的互斥量有优先级继承机制,所以需要确保正确配置。可能还需要提醒用户注意递归次数,避免栈溢出,以及确保在同一个任务中获取和释放。 参考中的引用[3]提到了互斥的使用,但用户的问题更深入,涉及到递归,所以需要扩展这部分内容。可能还需要比较递归和普通互斥的区别,帮助用户理解何时该使用递归。 最后,生成相关问题时,要考虑用户可能进一步想了解递归的实现原理、与普通的性能差异,或者实际应用案例。这些问题可以帮助用户更全面地掌握递归的使用。</think>### FreeRTOS递归使用方法及常见问题解决 #### 一、递归核心原理 递归(Recursive Mutex)允许同一个任务多次获取资源而不会导致死,每次获取后必须释放相同次数才能完全释放。其核心特性通过计数器实现: $$ \text{获取次数} = \text{释放次数} $$ 当计数器归零时,才真正释放[^3]。 #### 二、具体实现步骤 1. **创建递归** ```c SemaphoreHandle_t xMutex = xSemaphoreCreateRecursiveMutex(); ``` > 必须使用`xSemaphoreCreateRecursiveMutex()`而非普通互斥量创建函数 2. **获取操作** ```c if(xSemaphoreTakeRecursive(xMutex, portMAX_DELAY) == pdTRUE) { // 临界区代码 } ``` 支持嵌套调用示例: ```c void recursive_func() { xSemaphoreTakeRecursive(xMutex, portMAX_DELAY); // 第一次获取 xSemaphoreTakeRecursive(xMutex, portMAX_DELAY); // 第二次获取(嵌套) // ...操作 xSemaphoreGiveRecursive(xMutex); // 释放第一次获取 xSemaphoreGiveRecursive(xMutex); // 释放第二次获取 } ``` 3. **释放操作** ```c xSemaphoreGiveRecursive(xMutex); ``` #### 三、常见错误解决方案 | 错误现象 | 原因分析 | 解决方案 | |---------|----------|----------| | 任务卡死在Take操作 | 使用普通Take函数操作递归 | 必须使用TakeRecursive/GiveRecursive函数对[^3] | | 系统返回errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY | 堆空间不足 | 增加FreeRTOS堆大小(configTOTAL_HEAP_SIZE)| | 释放后仍无法被其他任务获取 | 释放次数不足 | 检查每个TakeRecursive都有对应的GiveRecursive | | 优先级反转问题 | 高优先级任务等待低优先级任务 | 确保使用`configUSE_MUTEXES = 1`启用优先级继承[^2] | #### 四、最佳实践建议 1. **代码结构规范** ```c void TaskFunction(void *pvParam) { for(;;) { if(xSemaphoreTakeRecursive(xMutex, pdMS_TO_TICKS(100))){ /* 临界区操作 */ xSemaphoreGiveRecursive(xMutex); } vTaskDelay(pdMS_TO_TICKS(10)); } } ``` 2. **调试技巧** - 使用`uxSemaphoreGetCount()`查看计数器状态 - 启用`configUSE_TRACE_FACILITY`获取详细信号量状态 - 设置断言检查`xSemaphoreTakeRecursive()`返回值 3. **性能优化** - 嵌套深度不宜超过3层 - 临界区执行时间应小于任务时间片的50% - 优先考虑读写方案替代递归
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值