iOS知识小集之main-queue!=main-thread

GCD main queuemain thread的关系


dispatch_get_main_queue()返回main queue,该队列会被绑定到main thread,所以我们如果我们将block提交到main queue,那么该block将会在主线程中执行。

dispatch_sync(queue, block)main queue提交block


1
2
3
4
5
6
7
/// Current thread is main thread
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSLog(@"Is main thread? %d", [NSThread isMainThread]);
});

/// output:
/// "Is main thread? 1"

大家有没有注意到,Log输出表明执行Block时的线程为主线程,根据我们以往的经验,dispatch_get_global_queue获取到的队列,队列中的Block应该是在Secondly thread中执行,为什么这里会是在主线程中呢。

答案就是在dispatch_sync语句,libdispatchcommit中进行了优化,只要目标queue不是main queue,那么提交的Block就会直接在原线程中执行,这就能解释为什么上面的demo程序中输出的Log显示是主线程。

那么,这种会不会有潜在的问题呢?

isMainThread带来的潜在问题


首先,我们看一个Radar,大概意思是,即使MapKit框架的addOverlay方法在主线程执行,但是由于其不是在main queue中执行的,会导致Crash,这是因为MapKit内部在main queue中使用dispatch_queue_set_specific设置了数据,当你在其它队列中执行时,会由于没有该数据从而导致Crash

避免使用isMainThread


所以,我们需要避免使用isMainThread,而是判断是不是main queue,我们可以使用两种方法来判断:

  1. 使用dispatch_queue_set_specificmain queue上设置flag,然后做判断,有该flag即为main queue.
  2. 判断dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL) == dispatch_queue_get_label(dispatch_get_main_queue())main queue设置了label,可以通过其来判断。

总结


  1. main queue中的Block一定在主线程中执行。
  2. 主线程可以对应多个queue,既可以有任意的队列,其Block可以在主线程中执行。

参考


  1. https://github.com/ReactiveCocoa/ReactiveCocoa/issues/2635#issuecomment-170215083
  2. http://www.openradar.me/24025596