GCD是Grand Central Dispatch的缩写,有人叫它大中枢派发、或者大中央调度,不管叫啥,总之,它是iOS开发的一个多线程编程解决方法,比起NSThread、NSOperationQueue、NSInvocationOperation等多线程技术方案,使用起来更加简单方便。
GCD的优点:1.GCD是苹果公司为多核的并行运算提出的解决方案
2.GCD会自动利用更多的CPU内核(比如双核、四核)
3.GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
4.程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码
一、基本概念理解
1、进程和线程的理解
1. 进程:正在进行中的程序被称为进程,负责程序运行的内存分配,每一个进程都有自己独立的虚拟内存空间。
2. 线程是进程中一个独立的执行路径,即主线程,主线程有1M的栈区,对于耗时的执行路径,可以放在子线程(512K栈区)中执行。
注意:(1)新建线程会消耗内存空间和CPU事件,线程太多会降低系统的运行性能,多线程是通过CPU时分复用实现的。
(2)多线程是为了并发执行多项任务,不会提高单个算法本身的执行效率。
2、并发(Concurrency),并行(Parallelism)的理解
举个形象的例子:A、B两个基佬某天相约去小树林-------挖坑,
1. A和B各要挖一个坑,他们只有一把铁锨,于是两人商量,A挖一下,把铁锨给B,B挖一下,再把铁锨给A,这样交替使用铁锨,最后两人各挖了一个大坑,却总共花了20分钟。这就是并发;
2. A和B各要挖一个坑,他们各有一把铁锨,同时挖坑,最后,每个人各花了大概10分钟时间挖完了大坑。这就是并行。
由此可见,并发与并行的区别:并行是严格意义上的同时执行,而并发并不是严格的同时执行,而是以时间片为单位交替执行,所以不需要多处理器。
3、同步(sync)、异步(async)的理解
举个形象的例子:A、B是一对情侣,某天某时A男为B女做好了饭,于是喊B女去吃饭
1. B女在看韩剧,A一直喊她去吃饭,可是B一直不去,没办法,A只能等到B女看完韩剧才能一起吃饭,真爱需要等待,真爱就是同步
2. B女在看韩剧,A喊了一声后,没管来不来,自己就去吃饭了,这就是异步
区别:等待与不等待
4、GCD中串行队列(Serial Dispatch Queue) 、并发队列(Concurrent Dispatch Queue)的理解
GCD使用了队列的概念,解决了NSThread难于管理的问题,可以把队列想象成数组,通常我们把要执行的任务放到队列中管理
不管是串行队列(SerialQueue)还是并发队列(ConcurrencyQueue),都是FIFO队列。也就意味着,任务一定是一个一个地,按照先进先出的顺序来执行。
1.串行队列:在创建队列时,传参数DISPATCH_QUEUE_SERIAL表示创建串行队列。任务会一个一个地执行,只有前一个任务执行完成,才会继续执行下一个任务。 串行执行并不是同步执行的意思,一定要注意区分
2.并发队列:在创建队列时,传参数DISPATCH_QUEUE_CONCURRENT表示创建并发队列。并发队列会尽可能多地创建线程去执行任务。并发队列中的任务会按入队的顺序执行任务,但是哪个任务先完成是不确定的。
Serial Dispatch Queue -- 线程池只提供一个线程用来执行任务,所以后一个任务必须等到前一个任务执行结束才能开始。
Concurrent Dispatch Queue -- 线程池提供多个线程来执行任务,所以可以按序启动多个任务并发执行。
二、GCD的使用
创建队列的方法
dispatch_queue_t q = dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)
参数:
const char *label:队列的名称
dispatch_queue_attr_t attr:队列的属性,属性有两个,分别为:
DISPATCH_QUEUE_SERIAL(NULL) 串行队列
DISPATCH_QUEUE_CONCURRENT 并发队列
队列属性为宏,其中串行队列的宏值为NULL,所以创建一个串行队列可以用如下代码
dispatch_queue_t q = dispatch_queue_create(“jianshu", NULL);
同步和异步代表会不会开辟新的线程。串行和并发代表任务执行的方式。
同步串行和同步并发,任务执行的方式是一样的。没有区别,因为没有开辟新的线程,所有的任务都是在一条线程里面执行。
异步串行和异步并发,任务执行的方式是有区别的,异步串行会开辟一条新的线程,队列中所有任务按照添加的顺序一个一个执行,异步并发会开辟多条线程,至于具体开辟多少条线程,是由系统决定的,但是所有的任务好像就是同时执行的一样。
1.串行队列异步任务
因为是异步:(1)会创建新的线程(2)主线程执行时间不确定
因为是串行队列:队列中所有任务按照添加的顺序一个一个执行
异步串行只会开辟一条新的线程去执行
2.并发队列异步任务
并行队列下地异步函数会开启N条子线程,且执行任务的顺序我们无法控制,至于是哪条线程执行任务由队列决定,哪个任务先完成由CPU决定。
3.串行队列同步任务
同步任务不会开辟新线程,所以所有的的任务都会在主线程上依次执行
4.并发队列同步任务
同步任务不会开辟新线程,所以所有的的任务都会在主线程上依次执行
5.主队列
在应用启动的时候,就会自动创建与主线程关联的串行队列,我们也可能获取,不能手动创建。主队列专门负责调度主线程度的任务,没有办法开辟新的线程。所以,在主队列下的任务不管是异步任务还是同步任务都不会开辟线程,任务只会在主线程顺序执行。
(1)主队列异步任务:现将任务放在主队列中,但是不是马上执行,等到主队列中的其它所有任务除我们使用代码添加到主队列的任务的任务都执行完毕之后才会执行我们使用代码添加的任务。
(2)主队列同步任务:容易阻塞主线程,所以不要这样写。原因:我们自己代码任务需要马上执行,但是主线程正在执行代码任务的方法体,因此代码任务就必须等待,而主线程又在等待代码任务的完成好去完成下面的任务,因此就形成了相互等待。整个主线程就被阻塞了。
提示:注意主线程是一直工作的,除非将程序杀掉,否则主线程的工作永远不会结束。
6.全局队列
是一种特殊的并行队列,获取全局队列的方法:dispatch_get_global_queue(long identifier,unsigned long flags)
参数1:代表该任务的优先级,默认写0就行,不要使用系统提供的枚举类型(见过好多人都使用DISPATCH_QUEUE_PRIORITY_DEFAULT),因为ios7和ios8的枚举数值不一样,使用数字可以通用。
#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
参数2:苹果保留关键字,一般也写0
全局队列和并发队列的区别:
(1)全局队列没有名字,但是并发队列有名字。有名字可以便于查看系统日志
(2)全局队列是所有应用程序共享的。
(3)在mrc的时候,全局队列不用手动释放,但是并发队列需要。
全局队列的使用:通常,我们可以在global_queue中做一些long-running的任务,完成后在main_queue中更新UI,避免UI阻塞,无法响应用户操作:
7.dispatch_group_async的使用
dispatch_group_async可以实现监听一组任务是否完成,完成后得到通知执行其他的操作。这个方法很有用,比如执行四个下载任务,当四个任务都下载完成后才通知界面说完成。
关于dispatch_group_async其他使用的方式此处暂不考虑
8.dispatch_barrier_async的使用
dispatch_barrier_async是在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行
说明dispatch_barrier_async的顺序执行还是依赖queue的类型啊,必需要queue的类型为dispatch_queue_create创建的,而且attr参数值必需是DISPATCH_QUEUE_CONCURRENT类型,前面两个非dispatch_barrier_async的类型的执行是依赖其本身的执行时间的,如果attr如果是DISPATCH_QUEUE_SERIAL时,那就完全是符合Serial queue的FIFO特征了。
9.dispatch_once
dispatch_once这个函数,它可以保证整个应用程序生命周期中某段代码只被执行一次!
10.dispatch_after
有时候需要等几秒钟后处理一些事情
注:动手练练有助于理解