优先级反转
什么是优先级反转
在前面我们举了一个共享资源竞争的例子:两个线程需要同时将同一个变量 V(初始值为 0)进行自增操作。解决共享资源竞争的办法是加入一把锁,在访问变量 V 前占有该锁,在访问后释放该锁。一般情况下,我们可以使用初始值为 TRUE 的二进制信号量或初始值为 1 的计数型信号量作为锁。
现在我们把这个例子稍加改动,有三个线程(线程 A、线程 B、线程 C)和一个变量 V,线程 A 和线程 B 需要同时访问变量 V。很显然我们需要一把锁(即保护变量 V 的锁 L)。
线程 A 的优先级为 1,线程 B 的为 3,线程 C 的为 2;即优先级:线程 A > 线程 > 线程 B。
我们假设现在线程 A 和线程 C 处于阻塞状态,线程 B 处于运行态。线程 B 占有了锁 L(下图中的#1),这时线程 C 等待的事件到来进入就绪状态,由于线程 C 的优先级较线程 B 的高,线程 C 将抢占线程 B 的执行(下图中的 #2);这时线程 A 等待的事件到来又进入就绪状态,由于线程 A 的优先级较线程 C 的高,线程 A 将抢占线程 C 并执行(下图中的#3);此时线程 A 也去申请锁 L,由于锁 L 已经被线程 B 占有,因此线程 A 必须阻塞等待锁 L 被线程 B 释放,而此时拥有中等优先级的线程 C 将继续运行(下图中的#4),此时 3 个线程的运行现状为:线程 A 阻塞、线程 C 正常运行、线程 B 阻塞,这显然有违 RTOS 的实时性原则。
一个高优先级线程通过信号量机制访问共享资源时,该信号量已被一个低优先级线程占有,而这个低优先级线程在访问共享资源时可能又被其他的一些中等优先级线程抢占,因此造成高优先级线程被许多具有较低优先级的线程阻塞,我们称此现象为优化级反转。
如何解决优先级反转
解决优先级反转问题有优先级天花板(priority ceiling)和优先级继承(priority inheritance)两种办法(SylixOS 互斥信号量同时支持)。
优先级天花板是当线程申请某共享资源时,将该线程的优先级提升到可访问这个资源的所有线程中的最高优先级,这个优先级称为该资源的天花板优先级。这种方法简单易行,不必进行复杂的判断,不管线程是否阻塞了高优先级线程的运行,只要线程访问共享资源都会提升线程的优先级。
优先级继承是当线程 A 申请共享资源 V 时,如果共享资源 V 正在被线程 B 使用,通过比较线程 B 与自身的优先级,若发现线程 B 的优先级小于自身的优先级,则将线程 B 的优先级提升到自身的优先级,线程 B 释放共享资源 V 后,再恢复线程 B 的原优先级。这种方法只在占有资源的低优先级线程阻塞了高优先级线程时才动态地改变线程的优先级。
二进制信号量和计数信号量均不支持优先级天花板和优先级继承,只有互斥信号量才支持优先级天花板和优先级继承。