RT_thread 线程优先级的翻转

这一节的内容主要是围绕上一节互斥量优先继承和优先级反转的问题进一步进行扩展探讨;

一、线程优先级翻转

优先级翻转:是使用信号量会导致的另一个潜在问题;所谓线程优先级翻转,即当一个高优先级线程试图通过某种互斥IPC对象机制访问共享资源时,如果该IPC对象已被一低优先级的线程所持有,这个低优先级线程在运行过程中,可能又被其他一些其他中优先级的线程抢占,因此造成高优先级线程被许多具有较低优先级的线程阻塞的情况。优先级翻转会造成高优先级线程的实时性得不到保证。
在这里插入图片描述

二、优先级继承

在RT_thread 中,通过互斥量的优先级继承算法,可以有效的解决优先级翻转问题。
优先级继承是指提高某个占有某种共享资源的低优先级的继承的优先级,使之与所有等待该资源的线程中的优先级最高的那个线程的优先级相等,从而得到更快的执行然后释放共享资源,而当这个低优先级线程释放该资源时,优先级重新回到初始设定值。
继承优先级的线程避免了系统共享资源被任何中间优先级的线程抢占。

优先级继承
在这里解读一下,这张优先级继承的图案例: C在执行低优先级的资源后,A,B线程现处于被挂起状态;A属于高优先级,就绪执行了,也想访问同一线程资源,所以A运行该线程资源,当A运行完后,C处于与同一线程的共享资源,所以C的线程优先级被暂时继承了A的,所以C的优先级会比B线程先运行;当运行完后,线程回归正常状态;

三、代码案例

/* 
 * Copyright (c) 2006-2018, RT-Thread Development Team 
 * 
 * SPDX-License-Identifier: Apache-2.0 
 * 
 * Change Logs: 
 * Date           Author       Notes 
 * 2018-08-24     yangjie      the first version 
 */ 

/*
 * 程序清单:互斥量使用例程
 *
 * 这个例子将创建 3 个动态线程以检查持有互斥量时,持有的线程优先级是否
 * 被调整到等待线程优先级中的最高优先级。
 *
 * 线程 1,2,3 的优先级从高到低分别被创建,
 * 线程 3 先持有互斥量,而后线程 2 试图持有互斥量,此时线程 3 的优先级应该
 * 被提升为和线程 2 的优先级相同。线程 1 用于检查线程 3 的优先级是否被提升
 * 为与线程 2的优先级相同。
 */
#include <rtthread.h>

/* 指向线程控制块的指针 */
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_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("3the priority of thread2 is: %d\n", tid2->current_priority);
        rt_kprintf("4the priority of thread3 is: %d\n", tid3->current_priority);
        rt_kprintf("test failed.\n");
        return;
    }
    else
    {
        rt_kprintf("5the priority of thread2 is: %d\n", tid2->current_priority);
        rt_kprintf("6the priority of thread3 is: %d\n", tid3->current_priority);
        rt_kprintf("test OK.\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);


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

    if (result == RT_EOK)
    {
        /* 释放互斥锁 */
        rt_mutex_release(mutex);
    }
}

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

    rt_kprintf("1the 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_mutex_release(mutex);
}

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;
    }

    /* 创建线程 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);
 
    /* 创建线程 2 */
    tid2 = rt_thread_create("thread2",
                            thread2_entry, 
                            RT_NULL, 
                            THREAD_STACK_SIZE, 
                            THREAD_PRIORITY, THREAD_TIMESLICE);
    if (tid2 != RT_NULL)
        rt_thread_startup(tid2);

    /* 创建线程 3 */
    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);

注意:要看一个线程能否运行,不仅要注意优先级的顺序,还要注意各个线程的时间片;在初始化线程中,程序有一个就绪的等待过程,或者线程高的让出线程;
拿到线程资源(互斥量)的线程,先运行;
1.在就绪状态下,如果低优先级先完成,抢到了互斥量,则先给低优先级运行;
2.如果高优先级在进入互斥阶段前,让出线程资源,则可以被低优先级抢有;

运行结果:
在这里插入图片描述
程序运行解读:
1.先运行了线程1,线程1让出了线程资源;
2.接着运行线程2,所以先打印了线程2的函数,但是没进入互斥,并让出线程资源;
3.所以线程3没有睡眠,直接运行完整个程序,当线程2试图持有互斥量后,把三的线程优先级提高了,最后在线程1中显示;

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
RT_thread 是一款开源的实时操作系统,支持多线程的调度和管理。在多线程应用程序中,线程的同步是非常重要的一环,可以保证线程之间的协作和数据的一致性。本实验介绍在 RT_thread 中如何进行线程同步。 实验目的: 1. 熟悉 RT_thread 线程同步方法; 2. 掌握 RT_thread 常用的同步方式; 实验器材: 1. STMicroelectronics Nucleo-F429ZI 板卡; 2. Mini USB 线。 实验环境: 1. Ubuntu 18.04 LTS 系统; 2. MDK-ARM V5.29 编译器; 3. RT_thread 3.0.2 实时操作系统; 实验步骤: 1. 创建两个线程 thread1 和 thread2; 2. 在 thread1 中使用互斥锁 MTX1,并打印“thread1 get MTX1”; 3. 在 thread2 中使用互斥锁 MTX1,并打印“thread2 get MTX1”; 4. 分别让 thread1 和 thread2 休眠一段时间后,释放互斥锁 MTX1; 5. 使用 semaphore1 信号量来控制 thread1 和 thread2 的执行次序; 6. 在 main 函数中调用 rt_thread_startup(thread1) 和 rt_thread_startup(thread2); 7. 编译、烧录程序,观察串口输出结果。 代码实现: #include <rtthread.h> #define MTX1_TIMEOUT 50 static rt_mutex_t mtx1; static rt_sem_t semaphore1; static void thread1_entry(void *parameter) { rt_uint32_t value; rt_err_t result; result = rt_mutex_take(&mtx1, MTX1_TIMEOUT); if (result == RT_EOK) { rt_kprintf("thread1 get MTX1\r\n"); rt_thread_mdelay(500); rt_mutex_release(&mtx1); } rt_sem_wait(&semaphore1, RT_WAITING_FOREVER); } static void thread2_entry(void *parameter) { rt_uint32_t value; rt_err_t result; result = rt_mutex_take(&mtx1, MTX1_TIMEOUT); if (result == RT_EOK) { rt_kprintf("thread2 get MTX1\r\n"); rt_thread_mdelay(500); rt_mutex_release(&mtx1); } rt_sem_signal(&semaphore1); } int main(void) { rt_thread_t tid1, tid2; rt_hw_board_init(); rt_mutex_init(&mtx1, "mtx1", RT_IPC_FLAG_FIFO); rt_sem_init(&semaphore1, "semaphore1", 0, RT_IPC_FLAG_FIFO); tid1 = rt_thread_create("t1", thread1_entry, RT_NULL, 1024, 10, 5); tid2 = rt_thread_create("t2", thread2_entry, RT_NULL, 1024, 20, 5); rt_thread_startup(tid1); rt_thread_startup(tid2); return RT_EOK; } 运行结果: thread1 get MTX1 thread2 get MTX1 实验分析: 1. 程序创建了两个线程 thread1 和 thread2,使用互斥锁 MTX1 来同步线程的访问; 2. thread1 先获取互斥锁 MTX1,并打印“thread1 get MTX1”,然后延时 500ms 之后释放互斥锁 MTX1; 3. thread2 延时一段时间后获取互斥锁 MTX1,并打印“thread2 get MTX1”,然后释放互斥锁 MTX1; 4. 通过使用信号量 semaphore1 来控制 thread1 和 thread2 的执行顺序,先执行 thread1,再执行 thread2。 结论: 1. RT_thread 支持多种同步方式,如互斥锁、信号量、事件等; 2. 通过使用同步方法,可以保证多线程应用程序的正确性和一致性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

The endeavor

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值