iOS 多线程 GCD (一)

GCD

GCD(Grand Central Dispatch) 是 Apple 开发的一个多核变编程的比较新的解决方法。是一个在线程池模式的基础上执行的并发任务。可用于多核的并行计算;可以自动利用更多的 CPU 内核;可以自动管理线程的生命周期,比如创建线程、调度任务、销毁线程,程序员只要告诉 GCD 要执行什么任务,不需要操心线程的管理。

任务和队列

在 GCD 中有两个概念:任务和队列。

任务

任务就是要在线程中执行的那段代码,在 GCD 中主要讲这段任务代码放到block中。
任务的执行主要有两种方式:同步执行 (sync) 和异步执行 (async)。这两者的主要区别是:是否需要等待队列中的任务执行结束,时候有开启新的线程的能力。

同步执行(sync):

同步添加任务到指定的队列,要等待队列中的其他任务执行完毕,才能够执行。只能在当前的线程中执行任务,不具备开启新的线程的能力。

异步执行(async):

异步添加任务到指定的队列,不用等待,就可以执行任务。可以在新的线程中执行任务,具备开启新的线程的能力。

需要注意的是:异步执行虽然具备开启新的线程的能力,但是不一定会开启新的线程。这还跟任务添加到的指定的队列的类型有关系。

队列

这里的队列是指用来存放任务的队列,新的任务总是被插入到队列的末尾,读取任务的时候总是从队列的头部开始。每次读取一个任务,就会从队列中释放掉一个任务。

在 GCD 中有两种队列:串行队列和并发队列。它们都遵循先进先出的原则。主要区别是执行顺序不同,开启的线程数不同。

串行队列

串行队列 (Serial Dispatch Queue) 每次只执行一个任务,任务一个接着一个的执行。只开启一个线程,一个任务执行完毕后,再开始执行下一个任务。

并发队列

并发队列 (Concurrent Dispatch Queue) 可以让多个任务并发(同时)执行。可以开启多个线程,同时执行任务。
并发队列的并发功能只在异步函数 (dispatch_async) 下才是有效的。

GCD 的使用

GCD 的使用主要分为两个步骤:

  1. 创建队列(串行队列或者是并发队列);
  2. 将任务追加到队列中,系统根据任务类型执行任务(同步执行或者异步执行)

队列的创建及获取

可以使用 dispatch_queue_create 来创建队列,需要传入两个参数,第一个参数是表示队列的唯一标识,可以为空;第二个参数用来标识是串行队列还是并发队列:

1
2
3
4
5
6

//串行队列
dispatch_queue_t queue = dispatch_queue_create("queueSerial", DISPATCH_QUEUE_SERIAL);

//并行队列
dispatch_queue_t queue = dispatch_queue_create("queueConcurrent", DISPATCH_QUEUE_CONCURRENT);

主队列(Main Dispatch Queue)

GCD提供了一种特殊的串行队列:主队列 (Main Dispatch Queue)。所有在主队列中的任务都会在主线程中执行。

1
2
//获取主队列
dispatch_queue_t queue = dispatch_get_main_queue();

全局并发队列(Global Dispatch Queue)

GCD默认提供了全局并发队列 (Glonal Dispatch Queue)。dispatch_get_global_queue 可以获取全局并发队列,需要传入两个参数。第一个参数标识队列的优先级,一般可用 DISPATCH_QUEUE_PRIORITY_DEFAULT。第二个参数暂时还没有实际的用途,可用0.

1
2
//获取全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

任务的创建

1
2
3
4
5
6
7
8
9
//创建同步执行任务
dispatch_sync(queue, ^{
//任务代码
});

//创建异步执行任务
dispatch_async(queue, ^{
//任务代码
});

任务有同步执行和异步执行,队列也有串行队列和并发队列,在串行队列中又有比较特殊的主队列,这样就会出现6种不同的组合方式:

同步执行 + 并发队列

没有开启新的线程,串行执行任务。
虽然并发队列可以开启多个线程,同时执行多个任务。但是同步执行的任务本身不能创建新的线程,只有当前的一个线程,所以也就不存在并发了。而且当前的线程只有等待当前的队列中正在执行的任务执行完毕之后才能继续顺序的执行接下来的任务。

同步执行 + 串行队列

没有开启新的线程,串行执行任务。

同步执行 + 主队列

没有开启新的线程,串行执行任务

异步执行 + 并发队列

有开启新的线程,并发执行任务。

异步执行 + 串行队列

有开启新的线程(1条),串行执行任务

异步执行 + 主队列

没有开启新的线程,串行执行任务

GCD 线程间的通信

在 iOS 开发中,会将UI刷新(点击、滚动、拖拽等)放在主线程,其他比较耗时的操作(图片下载、文件上传等)放在其他线程。在其他线程中完成了耗时操作之后需要再次返回主线程,此时就需要线程见得通信了。

1
2
3
4
5
6
7
8
9
10

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0) , ^{
在这里完成一些耗时操作

//回到主线程
dispatch_async(dispatch_get_main_queue() , ^{
刷新UI的一些操作
});

});