GCD是iOS开发过程当中使用的比较频繁也比较重要的一块,往往项目里涉及到这方面的代码基本都是核心功能,维护的时候都小心翼翼,如果这里出问题给调试带来的难度是较大的。
下面我就来回顾温习一下GCD有关知识,如有纰漏和不足,欢迎指正。
本文主要通过一下几个模块来进行总结:
(1)多线程概念;
(2)几种多线程编程的优缺点;
(3) GCD中的几种队列类型;
(4)GCD中系统提供的常用dispatch方法;
多线程相关概念
1. 线程与进程:
进程:是指程序在计算机上的一次执行过程,例如开启微信,就是开启了一个进程,进程可包含多个线程。
线程:一组独立执行的代码段,一个线程同时只能执行一个任务,故而多线程并发就是指在同一时间执行多个任务。
iOS程序中,主线程(又称为UI线程)主要任务就是显示和刷新UI,也只有主线程可以直接修改UI,其他耗时操作会放在子线程(又称为异步线程)处理,这样可以提高程序的执行效率,提高资源的利用率。
主线程的堆栈大小是1M,其他线程开都是512KB,而且这个值不可更改,这也就意味着只要开启一个线程就会占用一部分内存,线程越多程序的性能也随之降低,所以一般不建议同时开启多个线程。
2. 线程按照执行方式又可细分:
同步线程:同步线程会阻塞当前线程然后开始执行该线程内的任务,任务执行完之后才会返回当前线程;
异步线程:异步线程不会阻塞当前线程,会开启其他线程去执行线程内的任务;
这里来说下串行和并发的概念:
串行队列:任务按先后顺序逐个执行,需要等待前一个任务执行完之后才开始执行下一个任务;
并发队列:多个任务按先后顺序一起开始执行,不用等待前一个任务执行完就可以执行下一个任务,因为任务添加的先后顺序间隔往往忽略不计,所以看起来像是一起执行的。
并发和并行也是两个不同的概念:并行是基于多核设备的,并行一定是并发,而并发不一定是并行。
3. 多线程编程中会经常遇到的问题:
死锁:两个或多个线程都在等待对方完成某个操作才能进行下一步操作,这时候就会发生死锁;
互斥锁:能够防止多线程抢夺资源造成的数据安全问题,但是需要消耗大量的资源;
原子属性(atomic)加锁:当属性以atomic修饰时,该属性就能支持互斥锁了,反之若以nonatomic修饰,表示忽略了线程安全;
上下文切换:当一个进程中有多个线程来回切换时,content switch用来记录执行状态,这是就会产生一些额外的开销。
几种多线程编程的优缺点
1. NSThread
优点:轻量级简单易用,可以直接操作线程对象;
缺点:需要自己管理线程的生命周期,线程同步;
2. NSOperation
优点:基于GCD对GCD的封装,比GCD更加面向对象,不需要关心线程管理;
缺点:使用时必须使用它的子类;
3. GCD
优点:基于C语言更底层更高效,并且不属于cocoa框架,简单易用,效率高,速度快,自动管理线程生命周期;
缺点:如果遇到较复杂的使用场景,很大可能会遇到死锁问题;
GCD中的几种队列类型
Dispatch block的执行最终都会放进某个队列中去执行,GCD中相关函数的创建一般都以dispatch开头:
(1)Mainqueue主线程串行队列:通过dispatch_get_main_queue来获取;
(2)Global queue 全局并发队列:通过dispatch_get_global_queue来获取,可以设置优先级;
(3)Custom queue 自定义队列:通过dispatch_queue_create来获取;
(4)Groupqueue 组队列:通过dispatch_group_create来获取;
GCD中系统提供的常用dispatch方法
1. Dispatch_after 延时添加到队列;
示例如下:
从打印信息来看Dispatch_after只是延时提交block,并没有延时后立即执行block,因此它不能实现精确控制。
2. Diapatch_apply :在给定的队列上多次执行某一任务,在主线程直接调用会阻塞主线程去执行block中的任务,所以一般把它放在异步队列中执行
示例如下:
3. Dispatch_once:保证在app运行期间,block中的代码只执行一次
比如一个经典的使用场景:单例
4. Dispatch_barrier_async :栅栏作用
在并发队列中,等待在Dispatch_barrier之前加入的队列全部执行完,再执行Dispatch_barrier中的任务,执行完之后再开始执行Dispatch_barrier之后加入到队列的任务。
示例如下: