GCD的优势:
- GCD 可用于多核的并行运算
- GCD 会自动利用更多的 CPU 内核(比如双核、四核)
- GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
- 程序员只需要告诉 GCD 想要执行什么任务,不需要编写任何线程管理代码
** GCD 任务和队列**
任务:在线程中执行操作代码,在 GCD 中是放在 block 中的。
执行任务有两种方式:
同步执行(sync)和异步执行(async)。
同步执行(sync):
同步添加任务到指定的队列中,在添加的任务执行结束之前,会一直等待,直到队列里面的任务完成之后再继续执行。
只能在当前线程中执行任务,不具备开启新线程的能力。
异步执行(async):
异步添加任务到指定的队列中,它不会做任何等待,可以继续执行任务。
可以在新的线程中执行任务,具备开启新线程的能力。
队列(Dispatch Queue):
队列指执行任务的等待队列,即用来存放任务的队列。队列是一种特殊的线性表,采用 FIFO(先进先出)
串行队列(Serial Dispatch Queue):
每次只有一个任务被执行。让任务一个接着一个地执行。(只开启一个线程,一个任务执行完毕后,再执行下一个任务)
并发队列(Concurrent Dispatch Queue):
可以让多个任务并发(同时)执行。(可以开启多个线程,并且同时执行任务)
并发队列的并发功能只有在异步(dispatch_async)函数下才有效
GCD 的使用步骤
- 创建一个队列(串行队列或并发队列)
- 将任务追加到任务的等待队列中,系统会根据任务类型执行任务(同步执行或异步执行)
特别注意
- 在主线程中调用同步执行 + 主队列 = 死锁
- 在子线程中调用同步执行 + 主队列 = 串行执行
GCD 线程间的通信
在其他线程中先执行任务,执行完了之后回到主线程执行主线程的相应操作
GCD 的其他方法
GCD 栅栏方法:dispatch_barrier_async
函数会等待前边追加到并发队列中的任务全部执行完毕之后,再将指定的任务追加到该异步队列中GCD 延时执行方法:dispatch_after
在指定时间之后将任务追加到主队列中。但时间并不精确GCD 一次性代码(只执行一次):dispatch_once
在创建单例、或者有整个程序运行过程中只执行一次的代码时,就用到了 GCD 的这个函数,能保证某段代码在程序运行过程中只被执行1次,并且即使在多线程的环境下,也可以保证线程安全。GCD 快速迭代方法:dispatch_apply
按照指定的次数将指定的任务追加到指定的队列中,并等待全部队列执行结束。串行队列中使用 dispatch_apply,就和for循环一样,按顺序同步执行。
异步并行队列使用,在多个线程中同时执行,顺序不可控。
GCD 队列组:dispatch_group
分别异步执行2个耗时任务,然后当2个耗时任务都执行完毕后再回到主线程执行任务。这时候我们可以用到 GCD 的队列组。调用队列组的 dispatch_group_async 先把任务放到队列中,然后将队列放入队列组中。或者使用队列组的 dispatch_group_enter、dispatch_group_leave组合 来实现dispatch_group_async。
调用队列组的 dispatch_group_notify 回到指定线程执行任务。或者使用 dispatch_group_wait 回到当前线程继续向下执行(会阻塞当前线程)。
dispatch_group_notify
监听 group 中任务的完成状态,当所有的任务都执行完成后,才执行dispatch_group_notify block中追加的任务。
dispatch_group_wait
暂停当前线程(阻塞当前线程),等待指定的 group 中的任务执行完成后,才会往下继续执行。
使用dispatch_group_wait 会阻塞当前线程。
dispatch_group_enter、dispatch_group_leave
dispatch_group_enter 标志着一个任务追加到 group,执行一次,相当于 group 中未执行完毕任务数+1
dispatch_group_leave 标志着一个任务离开了 group,执行一次,相当于 group 中未执行完毕任务数-1。
当 group 中未执行完毕任务数为0的时候,才会使dispatch_group_wait解除阻塞,以及执行追加到dispatch_group_notify中的任务。
dispatch_group_enter、dispatch_group_leave组合,其实等同于dispatch_group_async。
GCD 信号量:dispatch_semaphore
GCD中的信号量是指 Dispatch Semaphore,是持有计数的信号。
计数为0时等待,不可通过。
计数为1或大于1时,计数减1且不等待,可通过。
Dispatch Semaphore 提供了三个函数。
- dispatch_semaphore_create:创建一个Semaphore并初始化信号的总量
- dispatch_semaphore_signal:发送一个信号,让信号总量加1
- dispatch_semaphore_wait:可以使总信号量减1,当信号总量为0时就会一直等待(阻塞所在线程),否则就可以正常执行。
注意:信号量的使用前提是:想清楚你需要处理哪个线程等待(阻塞),又要哪个线程继续执行,然后使用信号量。
Dispatch Semaphore 线程同步
- 保持线程同步,将异步执行任务转换为同步执行任务
- 保证线程安全,为线程加锁
异步执行耗时任务,并使用异步执行的结果进行一些额外的操作。== 将异步执行任务转换为同步执行任务。
AFURLSessionManager.m 里面的 tasksForKeyPath: 方法。通过引入信号量的方式,等待异步执行任务结果,获取到 tasks,然后再返回该 tasks。
线程安全(使用 semaphore 加锁)
semaphore 数为1线程安全加锁
dispatch_semaphore_create 创建一个信号量,传入数字为信号总量。到0位锁
dispatch_semaphore_signal 信号量+1 相当于解锁
dispatch_semaphore_wait 信号量-1 相当于加锁