linux是如何解决优先级反转问题的?
发布网友
发布时间:2024-10-04 00:19
我来回答
共1个回答
热心网友
时间:2024-10-04 02:12
从一个线上问题说起,最近在线上遇到了一些[HMDConfigManager remoteConfigWithAppID:]卡死的情况。通过观察发现,出问题的线程都有QOS:BACKGROUND标记。整体看起来持有锁的子线程仍然在执行,只是留给主线程的时间不够了。这种现象可能是优先级反转导致的,即持有读写锁且优先级低的线程迟迟得不到调度,而具有高优先级的线程由于拿不到读写锁,一直被阻塞,从而导致互相死锁。
解决这个问题的关键在于调整队列的优先级。在iOS8之后引入了QualityOfService的概念,类似于线程的优先级,通过设置不同的QualityOfService的值,可以分配不同的CPU时间、网络资源和硬盘资源等。因此,我们可以尝试通过调整队列的优先级来解决优先级反转问题。
具体解决方法有两种:一种是去除对NSOperationQueue的优先级设置,遵循苹果的建议,不要随意修改线程的优先级,特别是存在高低优先级线程之间存在临界资源竞争的情况。另一种方法是在线程通过pthread_rwlock_wrlock拿到_rwlock的时候,临时提升其优先级,在释放_rwlock之后,恢复其原先的优先级。这里需要注意的是,这种方法只能使用pthread的api,不能使用NSThread提供的API。
为了验证上述的手动调整线程优先级是否有效,可以通过本地实验进行测试。定义了2000个operation(目的是为了CPU繁忙),优先级设置为NSQualityOfServiceUserInitiated,且对其中可以被100整除的operation的优先级调整为NSQualityOfServiceBackground,在每个operation执行相同的耗时任务,然后对这被选中的10个operation进行耗时统计。
统计结果显示,通过手动调整其优先级,低优先级任务的整体耗时得到大幅度的降低,这样在持有锁的情况下,可以减少对主线程的阻塞时间。最终上线验证,通过调整优先级解决了线上遇到的问题,卡死情况得到了缓解。
通过深入理解优先级反转,可以发现它是指某同步资源被较低优先级的进程/线程所拥有,较高优先级的进程/线程竞争该同步资源未获得该资源,导致较高优先级进程/线程反而推迟被调度执行的现象。根据阻塞类型的不同,优先级反转可以分为Bounded priority inversion和Unbounded priority inversion。
解决Unbounded priority inversion有两种方法:优先权极限(priority ceiling protocol)和优先级继承(priority inheritance)。这两种方法都会在释放锁的时候,恢复低优先级任务的优先级,但无法阻止Bounded priority inversion。要避免优先级反转,可以通过QoS传递在不同线程(或queue)间传递QoS。
在iOS系统中,优先级反转可以通过系统API来触发优先级反转避免机制。在发生优先级反转时,如果系统知道block所在的目标线程,系统会通过提高相关线程的优先级来解决优先级反转的问题。如果系统不知道block所在的目标线程,则无法知道应该提高谁的优先级,也就无法解决反转问题。
通过深入调研各种基础系统API,如pthread mutex、os_unfair_lock、pthread_rwlock_t、dispatch_sync、dispatch_wait、dispatch_semaphore等,可以验证这些API在解决优先级反转问题时的表现。通过验证可以发现,不同API在处理优先级反转时有不同表现,例如pthread mutex、os_unfair_lock和dispatch_sync等API在处理优先级反转时能够提升线程优先级,而dispatch_semaphore则无法避免优先级反转。
在字节跳动APM团队,我们致力于提升整个集团内全系产品的性能和稳定性表现,工作内容包括但不限于性能稳定性监控、问题排查、深度优化、防劣化等。我们期望为业界输出更多更有建设性的问题发现和深度优化手段。如果你对字节APM团队职位感兴趣,欢迎投递简历至邮箱 xushuangqing@bytedance.com。