RT-Thread学习笔记七——线程间通讯(互斥量)

1.优先级翻转问题

当rt-thread实时操作系统运行时,如果单纯使用信号量来管理公共资源,则会出现优先级翻转问题,导致高优先级线程被低优先级线程运行而阻塞,使得系统的实时性无法得到保证。下面为优先级翻转的具体解释

优先级翻转:

现有三个不同优先级的线程·,优先级分别为高,中,低,使用信号量机制访问同一公共资源(临界区)。

1.优先级为低的线程先占用信号量,访问公共资源,此时高优先级线程使用信号量访问临界区,由于低优先级线程已经占用信号量还未释放,则高优先级线程进入阻塞状态,等待信号量释放。

RT-Thread学习笔记七——线程间通讯(互斥量)_第1张图片

2.此时中优先级线程就绪,由于RT-Thread使用线程优先级抢占机制,则中优先级线程获得cpu使用权,低优先级线程挂起等待。

RT-Thread学习笔记七——线程间通讯(互斥量)_第2张图片

 3.当中优先级线程运行完毕,让出cpu使用权,低优先级线程得以继续运行。当c优先级线程运行完毕,释放信号量,则高优先级线程获取信号量访问临界区,开始运行。

RT-Thread学习笔记七——线程间通讯(互斥量)_第3张图片

 由此可见,此时高优先级线程没有立即获取cpu使用权,而是一直等待其他低优先级线程完成任务后释放信号量,才可运行,严重影响了系统的实时性。

2.优先级继承机制

为解决使用信号量优先级翻转问题,互斥量采用了优先级继承机制,下面为优先级继承机制的解释。

        当有线程通过互斥量访问某一公共资源(临界区)时,此时若有多个线程访问此临界区,则进入线程等待队列,此时将占用临界区的线程优先级暂时调整为线程等待队列中优先级最高的线程相同优先级,则当外界即使有其他线程需要使用cpu时,也不会影响线程等待队列中的线程访问临界区,避免其他中等优先级线程抢占cpu,当此线程使用完毕释放互斥量时,优先级回到原先优先级。

3.互斥量基本概念

互斥量是一种特殊的信号量,只有两种状态,开锁和闭锁,他适用于线程多次持有信号量,造成线程递归而死锁以及多个线程的优先级翻转问题。

互斥量初始化时为开锁状态,仅允许一个线程持有,当有线程通过互斥量访问临界区时,互斥量变为闭锁状态,此时其他线程若访问互斥量,则根据设定的时间进行等待,直到互斥量被释放后获取互斥量。

互斥量机制以此保证同一时刻只有一个线程访问临界区。

典型例子:串口通讯时,由于硬件资源只有一个,如果两个线程需要同时发送,则必须加上互斥锁。

ps:互斥量不能在中断服务函数中使用。

RT-Thread学习笔记七——线程间通讯(互斥量)_第4张图片

 高优先级和低优先级使用互斥量访问临界区的过程 

4.互斥量API 

互斥量与线程,信号量类似,分为静态和动态,区别为是否由系统分配内存空间,API同样有静态和动态互斥量的创建和删除,互斥量的获取和释放。

4.1静态互斥量

   4.1.1静态互斥量创建

rt_mutex_init	(	rt_mutex_t 	mutex,   //指向互斥量的指针控制块
                    const char * 	name,  //互斥量名称
                    rt_uint8_t 	flag     //互斥量标志位
)	

RT-Thread学习笔记七——线程间通讯(互斥量)_第5张图片

 使用时需先创建一个静态互斥量指针(static rt_mutex_t dynamic_mutex = RT_NULL;)之后再进行互斥量的创建就可以了。

  4.1.2 静态互斥量的删除

rt_mutex_detach	(rt_mutex_t  mutex)	   //互斥量的删除

RT-Thread学习笔记七——线程间通讯(互斥量)_第6张图片

 互斥量的删除函数,参数为指向互斥量的指针

4.2动态互斥量

4.2.1 动态互斥量的创建

rt_mutex_create	(	const char * 	name,   //互斥量名称
                     rt_uint8_t 	flag    //互斥量标志位
)	

RT-Thread学习笔记七——线程间通讯(互斥量)_第7张图片

 使用时也需要首先创建一个指向互斥量的指针(static rt_mutex_t dynamic_mutex = RT_NULL;),之后再创建互斥量即可。使用示例如下:

static rt_mutex_t dynamic_mutex = RT_NULL;
 /* 创建一个动态互斥量 */
    dynamic_mutex = rt_mutex_create("dmutex", RT_IPC_FLAG_FIFO);

 4.2.2 动态互斥量的删除

rt_mutex_delete	(	rt_mutex_t 	mutex	)	

RT-Thread学习笔记七——线程间通讯(互斥量)_第8张图片

互斥量的删除,参数为指向互斥量的指针。

4.3 互斥量的获取和释放

  4.3.1互斥量的获取

rt_mutex_take	(	rt_mutex_t 	mutex,  //互斥量指针
                    rt_int32_t 	time    //获取互斥量等待时间
)	 

RT-Thread学习笔记七——线程间通讯(互斥量)_第9张图片

互斥量的获取无需创建指针,直接调用代码即可。

  4.3.2互斥量的释放

rt_mutex_release(rt_mutex_t mutex)	 //互斥量对应指针

RT-Thread学习笔记七——线程间通讯(互斥量)_第10张图片

 互斥量的释放与获取相同,直接调用代码即可。

5.实际使用

/*
 * 程序清单:互斥锁例程
 *
 * 互斥锁是一种保护共享资源的方法。当一个线程拥有互斥锁的时候,
 * 可以保护共享资源不被其他线程破坏。线程1对2个number分别进行加1操作
 * 线程2也会对2个number分别进行加1操作。使用互斥量保证2个number值保持一致
 */
#include 
/* 指向互斥量的指针 */
static rt_mutex_t dynamic_mutex = RT_NULL;

static rt_uint8_t number1, number2 = 0;
ALIGN(RT_ALIGN_SIZE)

static void rt_thread_entry1(void *parameter)
{
    while (1)
    {
        /* 线程1获取到互斥量后,先后对number1、number2进行加1操作,然后释放互斥量 */
        rt_mutex_take(dynamic_mutex, RT_WAITING_FOREVER);
        number1++;
        rt_thread_mdelay(10);
        number2++;
        rt_mutex_release(dynamic_mutex);
    }
}


static void rt_thread_entry2(void *parameter)
{
    while (1)
    {
        /* 线程2获取到互斥量后,检查number1、number2的值是否相同,相同则表示mutex起到了锁的作用 */
        rt_mutex_take(dynamic_mutex, RT_WAITING_FOREVER);
        if (number1 != number2)
        {
            rt_kprintf("not protect.number1 = %d, mumber2 = %d \n", number1, number2);
        }
        else
        {
            rt_kprintf("mutex protect ,number1 = mumber2 is %d\n", number1);
        }
        number1++;
        number2++;
        rt_mutex_release(dynamic_mutex);
        if (number1 >= 50)
            return;
    }
}
/* 互斥量示例的初始化 */
int main(void)
{
    /* 创建一个动态互斥量 */
    dynamic_mutex = rt_mutex_create("dmutex", RT_IPC_FLAG_FIFO);
    if (dynamic_mutex == RT_NULL)
    {
        rt_kprintf("create dynamic mutex failed.\n");
        return -1;
    }

    static  rt_thread_t thread1=RT_NULL;
    thread1=rt_thread_create("thread1",
                   rt_thread_entry1,
                   RT_NULL,
                   256,
                   5, 5);
    rt_thread_startup(thread1);

    static  rt_thread_t thread2=RT_NULL;
    thread2=rt_thread_create("thread2",
                   rt_thread_entry2,
                   RT_NULL,
                   256,
                   4,
                   5);
    rt_thread_startup(thread2);

    return 0;
}

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

参考RT-Thread官方例程,做了一些修改,改为两个动态线程使用互斥量。现象如下:

RT-Thread学习笔记七——线程间通讯(互斥量)_第11张图片  RT-Thread学习笔记七——线程间通讯(互斥量)_第12张图片

串口输出,从1到49。

你可能感兴趣的:(RT-Thread,Studio,STM32单片机学习,学习)