问题
最近跑项目的时候发现了一个紫色的警告⚠️,只有在Run阶段才有。
Thread running at QOS_CLASS_USER_INITIATED waiting on a lower QoS thread running at QOS_CLASS_DEFAULT. Investigate ways to avoid priority inversions
分析
警告翻译过来的意思是:运行在QOS_CLASS_USER_INITIATED的线程正在等待运行在QOS_CLASS_DEFAULT的低QoS线程。研究避免优先级反转的方法
显然这是个优先级反转的问题,关于QoS,Foundation框架中有个枚举
typedef NS_ENUM(NSInteger, NSQualityOfService) {
NSQualityOfServiceUserInteractive = 0x21,
NSQualityOfServiceUserInitiated = 0x19,
NSQualityOfServiceUtility = 0x11,
NSQualityOfServiceBackground = 0x09,
NSQualityOfServiceDefault = -1
} API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0));
其中NSQualityOfServiceDefault和NSQualityOfServiceUserInitiated恰好和警告中的QOS_CLASS_DEFAULT和QOS_CLASS_USER_INITIATED对应。
警告发生的地方运行的线程的qos为QOS_CLASS_USER_INITIATED,但在[[BMKMapManager alloc] init]里面有等待其他线程qos为QOS_CLASS_DEFAULT的操作。
cpu在分配权限的时候会根据qos的等级优先分配,QOS_CLASS_USER_INITIATED的级别比QOS_CLASS_DEFAULT更高,会抢QOS_CLASS_DEFAULT的资源,但它抢到资源也没有什么用,因为需要等待
QOS_CLASS_DEFAULT中的操作完成才行,在资源紧张的情况下,系统把资源都给了QOS_CLASS_DEFAULT,没有足够资源给QOS_CLASS_DEFAULT,导致互相等待。
尝试复现
尝试写一个类似的警告
- (void)hangRiskTest {
//----主线程----
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.qualityOfService = NSQualityOfServiceUserInitiated;
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(runInQoSInit) object:nil];
[queue addOperation:op];
}
- (void)runInQoSInit {
self.semaphore = dispatch_semaphore_create(0);
// ---- QOS_CLASS_USER_INITIATED ---
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.qualityOfService = NSQualityOfServiceDefault;
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
// ----NSQualityOfServiceDefault
NSLog(@"hangRiskTest_111");
usleep(arc4random_uniform(30));
dispatch_semaphore_signal(self.semaphore);
NSLog(@"hangRiskTest_222");
}];
[queue addOperation:op];
dispatch_wait(self.semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"hangRiskTest_3333");
}
等待代码运行过这段代码后,就会出现这个警告。注意,这种蓝色的警告只有在程序运行完才能显示。
runInQoSInit方法里面的代码是运行在优先级为NSQualityOfServiceUserInitiated线程中的,相对而言,这个优先级是比较高的。159-164行代码是运行在优先级为NSQualityOfServiceDefault线程中的。但两个线程都要操作信号量semaphore,低优先级的线程阻塞了高优先级代码的运行,这种情形就会出现警告。
结尾
回过头看项目上的警告,目前看是没有办法消除的,但通过对该问题的探究,对线程的优先级有了进一步的了解,还是多多少少有点收获的,特此记录,便于日后回顾,欢迎各位看官点赞评论。