优先级反转问题及解决办法
- 什么是优先级反转
- 解决方法1:优先级继承
- 解决方法2:优先级天花板
- 总结
什么是优先级反转
优先级反转,是指在多线程的环境下,并且使用了信号量时,可能会出现的这样一种不合理的现象,即:
高优先级任务被低优先级任务阻塞,导致高优先级任务迟迟得不到调度。但其他中等优先级的任务却能抢到CPU资源。从现象上来看,好像是中优先级的任务比高优先级任务具有更高的优先权。
具体来说:当高优先级任务正等待信号量(此信号量被一个低优先级任务拥有着)的时候,一个介于两个任务优先之间的中等优先级任务开始执行——这就会导致一个高优先级任务在等待一个低优先级任务,而低优先级任务却无法执行类似死锁的情形发生。
一个具体的例子:假定一个进程中有三个线程Thread1(高)、Thread2(中)和Thread3(低),考虑下图的执行情况:
- T0时刻,Thread3运行,并获得同步资源SYNCH1;
- T1时刻,Thread2开始运行,由于优先级高于Thread3,Thread3被抢占(Thread3未释放同步资源SYNCH1),Thread2被调度执行;
- T2时刻,Thread1抢占Thread2,但Thread1需要同步资源SYNCH1,而SYNCH1被更低优先级的Thread3所拥有,Thread1被挂起等待该资源。而此时线程Thread2和Thread3都处于可运行状态,Thread2的优先级大于Thread3的优先级,所以Thread2被调度执行。
- 最终的结果是高优先级的Thread1迟迟无法得到调度,而中优先级的Thread2却能抢到CPU资源。
上述现象中,优先级最高的Thread1要得到调度,不仅需要等Thread3释放同步资源(这个很正常),而且还需要等待另外一个毫不相关的中优先级线程Thread2执行完成(这个就不合理了),会导致线程调度的实时性很差。
解决方法1:优先级继承
优先级继承就是为了解决优先级反转问题而提出的一种优化机制。
其大致原理是让低优先级线程在获得同步资源的时候(如果有高优先级的线程也需要使用该同步资源时),临时提升其优先级。以前其能更快的执行并释放同步资源。释放同步资源后再恢复其原来的优先级。
带有优先级继承调度过程:
- 与上图相比,到了T3时刻,Thread1需要Thread3占用的同步资源SYNCH1,操作系统检测到这种情况后,就把 Thread3的优先级提高到Thread1的优先级。
- 此时处于可运行状态的线程Thread2和Thread3中,Thread3的优先级大于Thread2的优先级,Thread3被调度执行。
- Thread3执行到T4时刻,释放了同步资源SYNCH1,操作系统恢复了Thread3的优先级,Thread1获得了同步资源SYNCH1,重新进入可执行队列。
- 处于可运行状态的线程Thread1和Thread2中,Thread1的优先级大于Thread2的优先级,所以Thread1被调度执行。
通过优先级继承机制,可以有效解决优先级反转问题,使优先级最高的Thread1获得执行的时机提前。
解决方法2:优先级天花板
优先级天花板是当线程申请某共享资源时,把该线程的优先级提升到可访问这个资源的所有线程中的最高优先级,这个优先级称为该资源的优先级天花板。
这种方法简单易行,不必进行复杂的判断,不管线程是否阻塞了高优先级线程的运行,只要线程访问共享资源都会提升线程的优先级。
总结
- (1)优先级反转提醒我们使用锁的代码段应尽量短;
- (2)注意用小锁代替大锁,减少冲突的机会;
- (3)如果锁保护的代码段很短,直接使用原子锁忙等也是不错的一个方法