RT_Thread_互斥量

1、互斥量概念

互斥量是一种特殊的二值信号量,只有两种状态:开锁或闭锁;可用于对临界资源的保护从而实现独占式访问。

当一个线程A持有互斥量时,其他线程不能进行开锁或持有,但线程A能够再次获得这个锁而不被挂起

这个特性与一般的二值信号量有很大的不同:在信号量中,线程递归持有会发生主动挂起,最终形成死锁。

信号量为1,take take release release,第一个take后信号量为0,第二个take就会挂起等待,而使得该信号量一直没机会释放造成死锁。

2、互斥量的特性——优先级继承

2.1、优先级翻转问题

信号量被低优先级线程C持有,而线程C在运行中被中等优先级线程B抢占,造成高优先级线程A被较低优先级的线程阻塞。

RT_Thread_互斥量_第1张图片

2.2、解决:优先级继承

线程 A 尝试获取共享资源而被挂起的期间,将线程 C 的优先级提升到线程 A 的优先级,防止C被B抢占,而当低优先级线程C释放该资源时,优先级重新回到初始设定。

这是一个过河拆桥的悲伤故事:当C拥有资源M的时候,如果大佬A也想要M,大佬A就假意带C混,此时A、C的优先级相同,但当C不再拥有资源M后,对A没什么用,立刻就被踢走C就滚回原先的优先级了。

RT_Thread_互斥量_第2张图片

3、互斥量API

3.1、创建和删除互斥量

  • 静态互斥量
rt_err_t rt_mutex_init (rt_mutex_t mutex, const char* name, rt_uint8_t flag);
参数 描述
mutex 互斥量对象的句柄,它由用户提供,并指向互斥量对象的内存块
name 互斥量的名称
flag 该标志已经作废,无论用户选择 RT_IPC_FLAG_PRIO 还是 RT_IPC_FLAG_FIFO,内核均按照 RT_IPC_FLAG_PRIO 处理
返回 ——
RT_EOK 初始化成功
rt_err_t rt_mutex_detach (rt_mutex_t mutex);
参数 描述
mutex 互斥量对象的句柄
返回 ——
RT_EOK 成功
  • 动态互斥量
rt_mutex_t rt_mutex_create (const char* name, rt_uint8_t flag);
参数 描述
name 互斥量的名称
flag 该标志已经作废,无论用户选择 RT_IPC_FLAG_PRIO 还是 RT_IPC_FLAG_FIFO,内核均按照 RT_IPC_FLAG_PRIO 处理
返回 ——
互斥量句柄 创建成功
RT_NULL 创建失败
rt_err_t rt_mutex_delete (rt_mutex_t mutex);
参数 描述
mutex 互斥量对象的句柄
返回 ——
RT_EOK 删除成功

3.2、获取互斥量

rt_err_t rt_mutex_take (rt_mutex_t mutex, rt_int32_t time);
参数 描述
mutex 互斥量对象的句柄
time 指定等待的时间
返回 ——
RT_EOK 成功获得互斥量
-RT_ETIMEOUT 超时
-RT_ERROR 获取失败
rt_err_t rt_mutex_trytake(rt_mutex_t mutex);
参数 描述
mutex 互斥量对象的句柄
返回 ——
RT_EOK 成功获得互斥量
-RT_ETIMEOUT 获取失败

3.3、释放互斥量

rt_err_t rt_mutex_release(rt_mutex_t mutex);
参数 描述
mutex 互斥量对象的句柄
返回 ——
RT_EOK 成功

4、优先级继承的示例代码及说明

初始状态:线程1优先级9,线程2优先级10,线程3优先级11;

  • 线程1先运行,线程1挂起100ms;
  • 线程2运行,线程2挂起50ms;
  • 线程3运行,线程3获取互斥量,并将持有500ms;
  • 50ms到了,切换线程2运行,线程2试图获取互斥量,此时互斥量被低优先级的线程3拥有,提升线程3优先级为10,线程2挂起,线程3接着运行;
  • 100ms到了,切换线程1运行,此时线程2和线程3的优先级都是10,线程1运行结束;
  • 线程3运行直到释放互斥量,线程3优先级恢复为11;
  • 切换高优先级线程2运行,获取互斥量,释放互斥量,线程2结束;
  • 线程3运行,打印当前优先级,线程3结束;

在官方的例程上做了一些修改,对执行的过程转变的了解更细致一些,只截图最后线程3又恢复优先级和线程2的切换;

RT_Thread_互斥量_第3张图片

#include 

/* 指向线程控制块的指针 */
static rt_thread_t tid1 = RT_NULL;
static rt_thread_t tid2 = RT_NULL;
static rt_thread_t tid3 = RT_NULL;
static rt_mutex_t mutex = RT_NULL;

#define THREAD_PRIORITY       10
#define THREAD_STACK_SIZE     512
#define THREAD_TIMESLICE      5

/* 线程 1 入口 */
static void thread1_entry(void *parameter)
{
	rt_kprintf("the priority of thread1 is: %d\n", tid1->current_priority);
    /* 先让低优先级线程运行 */
    rt_thread_mdelay(100);

    /* 此时 thread3 持有 mutex,并且 thread2 等待持有 mutex */

    /* 检查 rt_kprintf("the producer generates a number: %d\n", array[set%MAXSEM]); 与 thread3 的优先级情况 */
    if (tid2->current_priority != tid3->current_priority)
    {
        /* 优先级不相同,测试失败 */
        rt_kprintf("the priority of thread2 is: %d\n", tid2->current_priority);
        rt_kprintf("the priority of thread3 is: %d\n", tid3->current_priority);
        rt_kprintf("test failed.\n");
        return;
    }
    else
    {
        rt_kprintf("the priority of thread2 is: %d\n", tid2->current_priority);
        rt_kprintf("the priority of thread3 is: %d\n", tid3->current_priority);
        rt_kprintf("test OK.\n");
    }
	rt_kprintf("thread1 exit\n");
}

/* 线程 2 入口 */
static void thread2_entry(void *parameter)
{
    rt_err_t result;

    rt_kprintf("the priority of thread2 is: %d\n", tid2->current_priority);

    /* 先让低优先级线程运行 */
    rt_thread_mdelay(50);
	rt_kprintf("thread2 try mutex\n");

    /*
     * 试图持有互斥锁,此时 thread3 持有,应把 thread3 的优先级提升
     * 到 thread2 相同的优先级
     */
    result = rt_mutex_take(mutex, RT_WAITING_FOREVER);

    if (result == RT_EOK)
    {
		rt_kprintf("thread2 try mutex ok\n");
        /* 释放互斥锁 */
        rt_mutex_release(mutex);
		rt_kprintf("thread2 release mutex\n");
    }
	rt_kprintf("thread2 exit\n");
}

/* 线程 3 入口 */
static void thread3_entry(void *parameter)
{
    rt_tick_t tick;
    rt_err_t result;

    rt_kprintf("the priority of thread3 is: %d\n", tid3->current_priority);

    result = rt_mutex_take(mutex, RT_WAITING_FOREVER);
    if (result != RT_EOK)
    {
        rt_kprintf("thread3 take a mutex, failed.\n");
    }

    /* 做一个长时间的循环,500ms */
    tick = rt_tick_get();
    while (rt_tick_get() - tick < (RT_TICK_PER_SECOND / 2)){
		rt_kprintf("thread3 is waiting 500ms.\n");
	}

    rt_mutex_release(mutex);
	rt_kprintf("the priority of thread3 is: %d\n", tid3->current_priority);
	rt_kprintf("thread3 exit\n");
}

int pri_inversion(void)
{
    /* 创建互斥锁 */
    mutex = rt_mutex_create("mutex", RT_IPC_FLAG_FIFO);
    if (mutex == RT_NULL){
        rt_kprintf("create dynamic mutex failed.\n");
        return -1;
    }

    /* 创建线程 */
    tid1 = rt_thread_create("thread1",thread1_entry, RT_NULL,
                            THREAD_STACK_SIZE,THREAD_PRIORITY - 1, THREAD_TIMESLICE);
    if (tid1 != RT_NULL)    rt_thread_startup(tid1);
 
    tid2 = rt_thread_create("thread2",thread2_entry, RT_NULL, 
                            THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE);
    if (tid2 != RT_NULL)    rt_thread_startup(tid2);

    tid3 = rt_thread_create("thread3",thread3_entry, RT_NULL, 
                            THREAD_STACK_SIZE, THREAD_PRIORITY + 1, THREAD_TIMESLICE);
    if (tid3 != RT_NULL)    rt_thread_startup(tid3);

    return 0;
}

/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(pri_inversion, pri_inversion sample);

你可能感兴趣的:(RT_Thread,rt_thread)