iOS App使用GCD导致的卡顿现象及解决方法

2020-01-21 06:33:45丽君

从用户态调用栈无法得出答案,内核可能的状态过于复杂。

Q: I know it's suggested that we create limited amount of serial queue,and use target queue probably. but what could happen if we don't follow that rule?

Apple 一直推荐自己创建 serial GCD queue 的时候,一定要控制数量,而且最好设置 target queue,否则会出现问题,但会出现什么问题我一直很好奇,这次借着机会一起问了。

A:


* 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 (-:

这段回复很有意思。阅读过 GCD 源码的同学会知道,所有默认创建的 GCD queue 都有一个优先级,但其实每个优先级对应两个 queue,比如一个是 default-priority, 那么另一个就是 default-priority-overcommit。dispatch_async 的时候,会首先将任务丢进 default-priority 队列,如果队列满了,就转而丢进 default-priority-overcommit。