最近在调试的过程中遇到一次界面卡死的情况,发现主线程出现了死锁。
上面的调用堆栈是来自一个
dispatch_async(dispatch_get_main_queue(), ^{
//下面一行调用死锁
dispatch_async(dispatch_get_main_queue(), ^{
});
});
理论上dispatch_async是不会出现死锁的情况。
这篇文章 http://mrpeak.cn/blog/ios-gcd-bottleneck/里,苹果的回复很有意思
* On macOS, where the system is happier to over commit, you end up with a thread explosion. That in turn can lead to problems running out of memory, running out of Mach ports, and so on.
* On iOS, which is not happy about over committing, you find that the latency between a block being queued and it running can skyrocket. This can, in turn, have knock-on effects. For example, the last time I looked at a problem like this I found that `NSOperationQueue` was dispatching blocks to the global queue for internal maintenance tasks, so when one subsystem within the app consumed all the dispatch worker threads other subsystems would just stall horribly.
Note: In the context of dispatch, an “over commit” is where the system had to allocate more threads to a queue then there are CPU cores. In theory this should never be necessary because work you dispatch to a queue should never block waiting for resources. In practice it’s unavoidable because, at a minimum, the work you queue can end up blocking on the VM subsystem.
Despite this, it’s still best to structure your code to avoid the need for over committing, especially when the over commit doesn’t buy you anything. For example, code like this:
group = dispatch_group_create();
for (url in urlsToFetch) {
dispatch_group_enter(group);
dispatch_async(dispatch_get_global_queue(…), ^{
… fetch `url` synchronously …
dispatch_group_leave(group);
});
}
dispatch_group_wait(group, …);
is horrible because it ties up 10 dispatch worker threads for a very long time without any benefit. And while this is an extreme example — from dispatch’s perspective, networking is /really/ slow — there are less extreme examples that are similarly problematic. From dispatch’s perspective, even the disk drive is slow (-:
将很耗时的任务放到并行队列里是一个不太明智的选择。
Mike Ash 的这篇文章 GCD Practicum 讲的很透彻,还有示例代码,看完后就不需要再补充什么了。
最后,我搜索了代码里关于dispatch_get_global_queue的调用,将一些耗时但又不那么重要的任务放到串行队列里,暂时没有再发现此类情况。