理解 iOS 开发中 GCD 相关的同步(synchronization)\ 异步(asynchronization),串行(serial)\ 并行(concurrency)概念
iOS 开发过程中,常常需要用到多线程技术,GCD 是常用的实现多线程的技术,其因简洁底层而备受欢迎。
GCD 技术中,同步(synchronization)\ 异步(asynchronization),串行(serial)\ 并行(concurrency)等概念常常令人迷惑,不好理解。本文将对这两对概念进行尽可能简单的阐释。
同步和异步是针对当前代码流(即当前线程)和加入队列中的任务之间执行顺序的关系而言的。以同步的方式向队列中添加任务会阻塞当前线程,直到同步到队列中的任务执行完毕返回后,才会接着执行当前的代码流;以异步的方式向队列中添加的任务则不会阻塞当前线程,将任务添加到队列中后,不用等待任务执行完毕,即刻执行当前代码流。举例说明如下:
同步(dispatch_sync):
dispatch_sync(myQueue,^{
printf("1"); //任务 1
});
printf("2");
dispatch_sync(myQueue,^{
printf("3"); //任务 3
});
printf("4");
上面的代码执行结果只有一种可能,即输出“1234”。
在将输出 1 的任务(即代码中任务 1)同步到队列 myQueue 中后,当前线程阻塞,等待任务 1 执行完毕并返回后(即输出 1 之后),才能接着执行当前线程,输出 2。输出 2 之后,当前代码流继续运行,将任务 3 再同步到队列 myQueue 中后,当前线程阻塞,等待任务 3 执行,输出 3,之后当前代码流继续运行输出 4。
这段代码的输出结果与 myQueue 是串行队列还是并行队列没有关系。
异步(dispatch_async):
dispatch_async(serialQueue,^{
printf("1"); //任务 1
});
printf("2");
dispatch_async(serialQueue,^{
printf("3"); //任务 3
});
printf("4");
上面代码执行结果可能为 “1234”、“1243”、“2134”、“2143”、“2413” 中的一种,2 始终在 4 前面,1 始终在 3 前面(注意:此例中的队列(serialQueue)为串行队列,串行队列的概念将在后文讲述),2 始终在 3 前面。
将任务 1 异步添加到队列 serialQueue 中之后,不需要等待其执行完毕,当前代码流就继续执行了。当前代码流输出 2 时,可能任务 1 还没执行,也可能执行完毕了,因此 1 可能在 2 前输出也可能在 2 后输出;
同理,将任务 3 异步添加到队列 serialQueue 中之后,不需要等待其执行完毕,当前代码流就继续执行。当前代码流输出 4 时,可能任务 3 还没执行,也可能执行完毕了,因此 3 可能在 4 前输出也可能在 4 后输出。
对于当前代码流,输出 2 一定在 输出 4 前执行,因此 2 必定在 4 前输出。
对于串行队列 serialQueue ,任务 1 一定在 任务 3 前执行,因此 1 必定在 3 前输出。
将任务 3 添加到队列 serialQueue 的操作必定在输出 2 后进行,因此 2 必定在 3 前输出。
串行和并行是针对一个队列中的多个任务执行顺序而言的。串行队列是指队列中的任务是一个接一个地执行的,队首的任务执行完毕后才能执行其后面的任务,直至执行队尾的任务;并行队列是指队列中的任务可以并发地执行,即开始执行队首的任务后,不必等其执行完毕就可以接着开始执行队首之后的任务,因此在某一时刻可能存在同时执行的多个任务。
创建串行队列的方法是:
dispatch_queue_t serialQueue = dispatch_queue_create("mySerialQueue", NULL);
或
dispatch_queue_t serialQueue = dispatch_queue_create("mySerialQueue", DISPATCH_QUEUE_SERIAL);
同时主线程的队列也是串行队列,获取方法:
dispatch_queue_t mainQueue = dispatch_get_main_queue();
创建并行队列的方法是:
dispatch_queue_t concurrentQueue = dispatch_queue_create("myConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
同时系统提供了获取系统并发队列的函数:
dispatch_queue_t dispatch_get_global_queue(long identifier, unsigned long flags);
第一个参数 identifier 的值宏定义可以为
DISPATCH_QUEUE_PRIORITY_HIGH、
DISPATCH_QUEUE_PRIORITY_DEFAULT、
DISPATCH_QUEUE_PRIORITY_LOW、
DISPATCH_QUEUE_PRIORITY_BACKGROUND,
四个参数值获取的并发队列优先级依次从高到低;第二个参数 flags为系统保留参数,应传入值 0。
混淆同步异步和并行串行的概念,是因为我们没有弄清楚它们分别针对的对象是什么,一旦明白了它们描述的对象的区别,就不会在混淆了。再次总结一下,同步异步描述的是当前线程或代码流是否要阻塞以等待加入队列的任务执行完毕:同步要阻塞当前线程;异步不会阻塞当前线程。串行和并行描述的时队列里各个任务是否可以并发执行:串行队列里的任务不能并发执行,只能一个接一个地执行,同一时刻该串行队列里的任务最多只有一个在执行;并发队列里的任务后面的任务不必等待前面的任务执行完毕再执行,可以多个同时执行,同一时刻该并行队列里的任务可以有多个正在执行。