卡洛斯的博客

主线程和主队列的关系

主线程和主队列的关系

先说结论

主队列只在主线程中被执行的,而主线程运行的是一个 runloop,不仅仅只有主队列的中的任务,还会处理 UI 的布局和绘制任务。

几个例子

一、自定义串行队列,同步执行。

- (void)someMethod {
dispatch_queue_t queue = dispatch_queue_create("com.kk", nil);
dispatch_sync(queue, ^{
NSLog(@"current thread = %@, curren queue = %@, main queue = %@", [NSThread currentThread], [NSString stringWithCString:dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL) encoding:NSUTF8StringEncoding], [NSString stringWithCString:dispatch_queue_get_label(dispatch_get_main_queue()) encoding:NSUTF8StringEncoding]);

});
}

------------------
current thread = <NSThread: 0x600002c0cf00>{number = 1, name = main}, curren queue = com.kk, main queue = com.apple.main-thread
这里是主线程但不是主队列。

二、主线程判断的几个方法

//下面这几种切换到主线程执行的方法, 你更喜欢哪种?有什么优缺点?
//方法1
if ([NSThread isMainThread]) {
//xxx
} else {
dispatch_sync(dispatch_get_main_queue(), ^{
//xxx
});
}
// 由于主队列里的任务只能在主线程中被执行的,如果是子线程执行这段代码,那子线程会等着主线程执行玩任务,才能往下。
// 如果是主线程执行这段代码,那就走 if 的分支了,所以这段代码既不会引起UI的崩溃,也不会造成死锁。
// 只不过这种写法就必须要等任务执行完,才能玩下走。

//方法2
if ([NSThread isMainThread]) {
//xxx
} else {
dispatch_async(dispatch_get_main_queue(), ^{
//xxx
});
}
// 大部分都是这么用的,也没有什么问题
// 但是主线程不一定能过只跑主队列的任务。当主线程在同步执行自定义串行队列的任务时,那此方法的判断就不对了,因为此时是主线程且是非主队列

//方法3
dispatch_async(dispatch_get_main_queue(), ^{
//xxx
});
// 始终异步放在下一个loop使用,会延迟执行时机

//方法4
if (dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL) == dispatch_queue_get_label(dispatch_get_main_queue())) {
//xxx
} else {
dispatch_async(dispatch_get_main_queue(), ^{
//xxx
});
}
// 方法二已经解释了,这里可以通过判断队列label的形式,可以保证任务一定是放在主队列中的。虽然方法二,也不会造成什么问题,但是方法四会更加符合预期(任务一定要在主队列中)

综合来说,方法 4 是更好的判断是否是主线程的方式。

苹果的一个bug

在苹果的MapKit框架中,有一个叫做addOverlay的方法,它在底层实现的时候,不仅仅要求代码执行在主线程上,
还要求执行在 GCD 的主队列上。这是一个极罕见的问题,但已经有人在使用 ReactiveCocoa 时踩到了坑,并提交了 issue。
苹果的 Developer Technology Support 承认这是一个 bug。
https://toutiao.io/posts/535857/app_preview

正如上面的方法二,使用起来不会有什么问题,但是在这里,如果把一些数据通过dispatch_set_specific 绑定到main queue中,这才会产生 bug。例如下面,
这个获取就是和 main queue 绑定的,如果仅仅在主线程访问,无法获取到数据。

var skr = DispatchSpecificKey("")
if Thread.isMainThread {
let v = DispatchQueue.main.getSpecific(key: skr)
} else {
DispatchQueue.main.sync {
let v = DispatchQueue.main.getSpecific(key: skr)
}
}

参考