RTT的线程同步篇——互斥量

野火RTT第20章互斥量
2018年12月29日
10:47

互斥量不能在中断服务程序中使用。

互斥量是特殊的二值信号量,其“特殊”在哪呢?互斥量不同于二值信号量的地方在于:互斥量具有独占性(所有权)、递归性、优先级继承机制。
二值信号量不能防止优先级翻转。

为啥要有互斥量,二值信号量不就够了?后来一想,这是互斥量的特殊性,也为了方便管理,还是从信号量里面独立出来吧。这样的话函数好调用、也简单化了。否则形参又要复杂了。
把互斥量从二值信号量独立起来,代码编写更简洁,就不用考虑什么兼容二值信号量了。发挥的空间就更大了。

我发现RTT的函数的形参很少,很简单,越简单越好用、维护越容易、越简洁越美越有生命力。
互斥量是特殊的二值信号量,支持防优先级翻转,递归访问,互斥所有权,实现对临界资源的独占式处理。
互斥量只有两种状态:开锁和闭锁。
闭锁:被线程占用时的状态。
开锁:被占用线程释放后的状态。
所有权或者独占式:占用该互斥量的线程具有该互斥量的所有权,且独占——其他线程不能开锁或闭锁。——这是我的!!!独,很独!这个互斥量的锁是我的,别人不能开也不能闭锁!我的,别的线程不能动我的锁!
递归性:当有别的线程访问了该互斥量后,但是持有该互斥量的线程可以再次获取这个锁而不会被挂起。
二值信号量擅长同步(线程之间、线程与中断之间),而互斥量擅长保护资源的互锁。
优先级继承算法:持有资源的线程优先级临时提高到等待该资源的所有线程中最高优先级的优先级。临时提高优先级的操作就叫做优先级继承。这是互斥量具有,而信号量不具有的。

优先级翻转:高优先级线程不能运行而低优先级可以运行的现象。

野火例程:
1.H优先级线程先运行第一次获取互斥量,进行互斥量所有权操作:互斥量所有者,闭锁,互斥量占用线程的原始优先级,互斥量持有计数+1。接着进入阻塞延时,跳到L优先级线程。
2.L优先级要获取这个互斥量,但是已经闭锁了,因为L获取是无限等待的,所以会将其阻塞:从系统优先级表干掉,将其线程插入互斥量自带的阻塞链表中(按照优先级插入的,因为创建互斥量时flag参数选的是优先级排列)。执行调度,跳到H线程。
3.H线程执行释放互斥量,此时H线程具有互斥量所有权,所以互斥量持有计数减1,于是持有计数为0,于是处于开锁状态,切换到等待互斥量最高优先级的线程来持有:恢复原优先级,恢复被阻塞的最高优先级线程,将互斥量所有权转给它。并执行调度。因为此时H线程还是最高优先级所以还是执行H线程。
3,跳过时间片。线程H重头运行,第2次获取互斥量。由于此时互斥量所有权属于L线程了,H线程被阻塞,L线程发生优先级继承操作。发生调度,执行L线程。(原来优先级继承发生在互斥量获取函数中,也就是有更高优先级获取闭锁的互斥量会发生持有者线程继承优先级)
4,L线程执行查询H线程是都正常获取与释放,打印出信息。L线程释放互斥量所有权:L线程优先级恢复原始值, 唤醒阻塞线程H。执行调度,切换到H线程。
5.H线程执行阻塞延时,切换到L线程。L线程执行更大的阻塞延时,再次切换到H线程。
6,H线程释放互斥量所有权:这是真正的释放,恢复优先级,没有阻塞线程也就是没有等待这个互斥量的线程(L还没有获取而阻塞,目前只是L线程是阻塞延时主动放弃CPU使用权),互斥量的值、优先级、持有计数值等全部恢复到互斥量创建时的初始值。于是实现真正的开锁。
7,H线程执行时间片调度。结束继续重头循环。第三次获取互斥量,再次持有这个互斥量。然后阻塞延时,但是L线程还处于阻塞延时状态,于是会去执行空闲线程,然后等H的阻塞延时到了再执行H线程。接着执行互斥量释放。因为没有等待这个互斥量的线程,所以这是真正的释放,恢复互斥量创建时的初始值。就这样不断的循环,直到L线程的阻塞延时时间到,处于就绪状态,只等H线程进入阻塞延时。
8,一旦H进入阻塞延时就要重复1-7的循环。

总结来说:(1)当高优先级线程去获取闭锁状态的互斥量时,会发生优先级继承:持有者暂时提升为高优先级线程的优先级。当互斥量持有者释放时,又会恢复初始优先级(这就是为啥互斥量控制块里有初始优先级和当前优先级的成员,是为了优先级继承保证优先级翻转危害发生最低做准备)。(2)只有没有等待互斥量的线程时,才会恢复创建互斥量时的初始状态。(3)持有者线程释放互斥量时如果有等待这个互斥量(被阻塞了)的话,会把所有权交给这些等待的线程的(根据互斥量创建时是优先级排队还是先进先出排队来交接、传递这个所有权)。

你可能感兴趣的:(RTT-RTOS)