优先级反转和优先级继承

iOS的锁中有一个自选锁OSSpinLock , 这个api被标记为不推荐 , 理由就是会产生优先级反转的问题, 当时并不是特别理解这个优先级反转 . 后来在看一本书 <算法之美> 里在讲进程调度,优先级调度算法时也提到优先级反转,打算深入了解一下.

在iOS 中,系统维护了 5 个不同的线程优先级/QoS: background,utility,default,user-initiated,user-interactive。高优先级线程始终会在低优先级线程前执行,一个线程不会受到比它更低优先级线程的干扰。这种线程调度算法会产生潜在的优先级反转问题,从而破坏了 spin lock。

具体来说,如果一个低优先级的线程获得锁并访问共享资源,这时一个高优先级的线程也尝试获得这个锁,它会处于 spin lock 的忙等状态从而占用大量 CPU。此时低优先级线程无法与高优先级线程争夺 CPU 时间,从而导致任务迟迟完不成、无法释放 lock。这并不只是理论上的问题,libobjc 已经遇到了很多次这个问题了,于是苹果的工程师停用了 OSSpinLock。

优先级高的持续占有cpu,直到获取到资源。低优先级的竞争不过cpu,但是拿着资源,无法使用。导致死锁。

1.  优先级反转(Priority Inversion)

优先级反转,是指某同步资源被较低优先级的进程/线程所拥有,较高优先级的进程/线程竞争该同步资源未获得该资源,而使得较高优先级进程/线程反而推迟被调度执行的现象。

假定一个进程中有三个线程Thread1、Thread2和Thread3,它们的优先级顺序是Priority(Thread1) > Priority(Thread2) > Priority(Thread3)。考虑图一的执行情况。

图一、优先级反转现象(无优先级继承)

1,T0时刻,只有低优先级任务Thread3处于可运行状态,运行过程中,Thread3拥有了一个互斥资源SYNCH1;
2,T1时刻,中优先级任务Thread2就绪进入可运行状态,由于优先级高于正在运行的Thread3,Thread3被抢占(未释放互斥资源SYNCH1),Thread2被调度执行;
3,T2时刻,高优先级任务Thread1就绪抢占Thread2;
4,T3时刻,Thread1请求互斥资源SYNCH1,但信号量已被占有,所以Thread1阻塞等待该资源,而此时处于可运行状态的线程Thread2和Thread3中,Thread2的优先级高于Thread3的优先级,Thread2被调度执行。

上述现象中,优先级最高的Thread1既要等优先级低的Thread2运行完,还要等优先级更低的Thread3运行完之后才能被调度,如果Thread2和Thread3执行的很费时的操作,显然Thread1的被调度时机就不能保证,整个实时调度的性能就很差了。

2. 优先级继承(Priority Inheritance)

为了解决上述由于优先级反转引起的问题,OS引入了优先级继承的解决方法。优先级继承也就是,高优先级进程ThreadHigh在等待低优先级的线程ThreadLow继承占用的竞争资源时,为了使ThreadHigh能够尽快获得调度运行,由操作系统把ThreadLow的优先级提高到一个很高的优先级,从而让ThreadLow优先参与调度,尽快让ThreadLow执行并释放调ThreadHigh欲获得的竞争资源,然后ThreadLow的优先级调整到继承前的水平,此时TH可获得竞争资源而继续执行。

有了优先级继承之后的上述现象的执行情况如图二所示。

图二、优先级继承执行情况

与图一比较,图二中,到了T3时刻,Thread1需要Thread3占用的互斥资源SYNCH1,操作系统检测到这种情况后,就把Thread3的优先级提高到一个很高的的优先级。此时处于可运行状态的线程Thread2和Thread3中,Thread3的优先级大于Thread2的优先级,Thread3被调度执行。

Thread3执行到T4时刻,释放了互斥资源SYNCH1,操作系统恢复了Thread3的优先级,Thread1获得了互斥资源SYNCH1,重新进入可执行队列。处于可运行状态的线程Thread1和Thread2中,Thread1的优先级大于Thread2的优先级,所以Thread1被调度执行。

上述机制,使优先级最高的Thread1获得执行的时机提前。

 

发布了101 篇原创文章 · 获赞 33 · 访问量 24万+

猜你喜欢

转载自blog.csdn.net/u014600626/article/details/104574487