GCD异步与同步任务执行顺序分析
在iOS开发中,理解GCD(Grand Central Dispatch)任务的执行顺序对编写高效和安全的多线程代码至关重要。本文将通过一段代码示例,分析在主线程和非主线程上执行异步和同步任务时的输出顺序。
示例代码
以下是一段使用GCD的示例代码:
- (void)run {
dispatch_queue_t worker_queue = dispatch_queue_create("worker", DISPATCH_QUEUE_SERIAL);
NSLog(@"0");
dispatch_async(worker_queue, ^{
NSLog(@"1");
});
dispatch_async(worker_queue, ^{
NSLog(@"2");
});
dispatch_sync(worker_queue, ^{
NSLog(@"3");
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"4");
});
});
NSLog(@"5");
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"6");
});
}
在主线程上运行
- 在主线程上运行时,
dispatch_sync
调用会阻塞主线程,直到“同步任务”完成,而此处的“同步任务”又是串行队列的最后添加的一个任务,所以要等到前面的任务完成,分析到此处,可以确定如下输出顺序:
0
1
2
3
4
和6
的输出先后顺序是一定的,4
一定在6
的前面输出,因为 main_queue 是一个串行队列,那么 5 与 “4,6”的顺序是怎样的呢 ?这取决于到底是谁先入队的,是NSLog(@"5");
先入队,还是NSLog(@"4");
。答案是NSLog(@"5");
先入队,因为- (void)run { }
是作为一个整体先入队的,当执行如下代码时,NSLog(@"5");
已经在 main_queue 中:dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"4"); });
所以最终的输出顺序如下:
0
1
2
3
5
4
6
在非主线程上运行
- 在非主线程上运行时,
dispatch_sync
不会阻塞主线程,但会阻塞当前的非主线程。同上面在主线程上执行一样的道理,首先可以确定0, 1, 2, 3
最先执行,并且 4 在 6 之前执行。 - 由于 dispatch_async 将任务提交给主线程队列,而主线程处理这些任务的时机具体不确定性,具体取决于主线程的调度,所以输出 5 可能在 4 和 6 的前面,后面,中间。
- 可能的输出结果如下:
0
1
2
3
5
4
6
0
1
2
3
4
5
6
0
1
2
3
4
6
5