理解GCD一些概念
队列:串行队列,执行完一个任务再执行下一个任务;并发队列,可以多个任务同时执行;
执行方式:同步方式,不开启新线程,在主线程执行;异步方式,开启新线程(当队列是主队列时,不开新线程);
最佳任务执行方案:异步方式+并发队列
多线程的死锁问题:不同队列的任务在相互等待完成;
耗时的任务应该放到后台去运行;
GCD
GCD全称是Grand Central Dispatch,是一套纯C的API;
GCD会自动利用更多的CPU内核;
GCD会自动管理线程的生命周期,使用block块来执行任务;
常用;
队列和任务
在了解GCD之前,我们先来了解GCD的核心:队列和任务;
队列
队列是用来放置任务的,分为串行队列和并发队列;
串行队列:任务执行方式是一个接着一个,只有一个任务完成了才会执行下一个任务,一个app中的主队列就是一个特殊串行队列;
并发队列:任务执行方式可以在同一时间执行多个任务;
任务
任务,就是我们要执行的代码块,方式有异步async和同步sync;
异步函数,会开启新线程(当队列是主队列的时候,不会开启新线程);
同步函数,不会开启新线程,就是说会在当前主线程中执行任务,会阻塞当前主线程;
GCD的使用
GCD的使用主要是队列与任务之间相互组合;
异步+串行:会创建新的线程,一个任务完成后接着下一个任务;
异步+并行:开启新的线程,任务可以同时执行,任务同时开启数量取决于cpu的调度;
异步+主队列:没有开启新线程,串行执行任务;
同步+串行:不会开启新的线程,并且会阻塞主线程,直到该任务完成;
同步+并行:不会开启新的线程,会阻塞主线程,直到函数内任务执行完毕,并发队列失效;
同步+主队列:相互之间等待对方完成,会造成死锁,避免使用该组合;
队列创建
串行队列的创建有2种方式:
1 | //DISPATCH_QUEUE_SERIAL,NULL |
并行队列创建:
1 | //DISPATCH_QUEUE_CONCURRENT |
获取主队列,主队列是一个特殊的串行队列,更新UI等一系列操作都必须在主队列中进行:
1 | dispatch_queue_t mainQueue = dispatch_get_main_queue(); |
配合同步函数和异步函数的使用
1 | dispatch_queue_t serial1 = dispatch_queue_create("serial1", DISPATCH_QUEUE_SERIAL); |
GCD中的死锁
1,在主线程中当使用同步+主队列的时候,会造成线程的死锁;
要避免死锁,队列不要使用主队列,换成其他串行队列或者并发队列;
1 | dispatch_queue_t mainQueue = dispatch_get_main_queue(); |
2,在子线程中使用同步+主队列的时,不会造成死锁.
GCD之间的通讯
1,一个线程的数据传递给另外一个线程;
2,一个线程开始执行依赖另外一个线程;
例子:
1,当你使用GCD进行下载图片而有需要在主线程中进行UI界面更新的时候,会用到线程之间的通讯;
2,线程2开启依赖线程1任务是否执行完毕
1 | dispatch_queue_t q1 = dispatch_queue_create("q1", DISPATCH_QUEUE_CONCURRENT); |
GCD其他方法
延迟函数:dispatch_after
延迟2秒执行block内的任务,严格来说,这个时间不是绝对正确的;
1 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ |
dispatch_once
通常被用在单例的创建;
1 | static dispatch_once_t onceToken; |
栅栏barrier
我们有时候需要异步来执行2组操作,但是却希望是第一组操作完成之后再执行另外一组;这个时候,我们就可以使用来barrier实现:
1 | dispatch_queue_t concurrentBarrier = dispatch_queue_create("barrier", DISPATCH_QUEUE_CONCURRENT); |
dispatch_apply
把一个任务提交到队列中多次执行,具体是串行还是并行由队列本身决定;
apply不会立即返回,在执行完毕之后才会返回,是同步的调用;
1 | dispatch_queue_t concurrent = dispatch_queue_create("concurrent", DISPATCH_QUEUE_CONCURRENT); |
可以看出,当队列是并发队列的时候,除了会在主线程中执行还会开启其他的线程;
当队列是串行队列的时候,只会在主线程执行,不会开启新的线程;
1 | dispatch_queue_t serial1 = dispatch_queue_create("serial1", DISPATCH_QUEUE_SERIAL); |
dispatch_group
有时候,我们会有这样的需求:分别要求执行2个耗时任务,在这2个耗时的任务完成之后,我们在回到主线程或者指定线程来进行下一步的操作,这个时候就需要用到GCD中的队列组;
1 | dispatch_group_t group = dispatch_group_create(); |
dispatch_group的enter和leave
使用达到上面相同效果,他们的使用必须要是配对出现
1 | dispatch_group_enter(group); |
1 | dispatch_group_t group2 = dispatch_group_create(); |
dispatch_wait
暂停当前线程(阻塞当前线程),等待指定的 group 中的任务执行完成后,才会往下继续执行.
1 | dispatch_group_t group = dispatch_group_create(); |