本文由快学吧个人写作,以任何方法转载请表明原文出处。
一、GCD的概念
1. 根本概念
GCD全称 : Grand Central Dispatch
意为 : 多线程优化技能。从称号来看,更加直接,称号本意是中心调度,也便是说GCD将线程的创立和毁掉,还有调用和堵塞全部归属到中心调度,不需求咱们来办理。
GCD全部都是由C言语编写,供给了反常强大的函数。由所以C言语编写,性能非常不错。
推出GCD的原因 :
- 苹果公司为了处理多核的并行运算。
- GCD会主动的利用更多的CPU内存,比方GCD是能够自己来调用双核或者四核的
- GCD能够主动的办理线程的生命周期,比方线程的创立、线程的调度、线程的毁掉。所以运用GCD就只需求告诉GCD需求做什么。
简略的说 :
GCD = 使命 + 行列(用来寄存使命的) + 函数(用来履行使命的)
2. GCD中的使命
dispatch_block_t mission_block = ^{
NSLog(@"这是一个GCD的使命");
};
GCD中所谓的使命便是一个block块。里边便是逻辑代码,或者说使命代码。
3. GCD中的行列
dispatch_queue_t jd_queue = dispatch_queue_create("jd_gcd_queue", NULL);
GCD中的行列,自身便是一个特其他线性表,遵从FIFO准则,也便是先进来的使命先履行,履行后使命就退出行列。
GCD的行列一共分为两种 : 串行行列和并发行列。
(1). 串行行列
串行行列,Serial Dispatch Queue
:
所谓串行,便是使命要一个接一个的排队履行,一个使命履行结束了,再履行下一个,不许插队,串行行列不会敞开新的线程。举个例子 : 串行行列就像一条出产线,使命就像出产线上的货品,一个货品接着一个货品,一个货品下去了,后边的货品才能下去。
(2). 并发行列
并发行列,Concurrent Dispatch Queue
:
所谓并发,便是多个使命能够一起履行,能够敞开多条线程,多个使命一起履行。并发行列只要在
dispatch_async
函数下才有用。
4. GCD中的函数
GCD中有两个常用的函数 : 异步函数和同步函数。
异步函数,敞开多线程
dispatch_async(jd_queue, mission_block);
同步函数,不敞开多线程
dispatch_sync(jd_queue, mission_block);
- 同步函数 :
同步函数不具有拓荒新线程的才能。同步函数会将使命增加到指定的行列中,在同步函数增加的使命没有履行完结之前,其他的使命都要处于等候的状况中,直到同步函数增加的使命履行完结之后,才会履行其他的使命。
- 异步函数 :
异步函数具有拓荒新线程的才能,可是否敞开新的线程,何时敞开新的线程则和行列相关。异步函数增加完使命之后不会等候使命履行结束,异步函数只管增加使命和履行使命,不论增加的使命是否现在有必要履行完结,不影响当时线程中其他使命的履行。
5. 举例
能够看到,关于同一个串行行列(行列的第二个参数是NULL,是串行行列),同一个使命 :
异步函数会敞开线程,由于number是3。
同步函数不会敞开线程,由于number是1,而且name是main,证明便是在主线程中履行的。
二、GCD的根本运用
1. 自定义GCD的行列创立
GCD创立行列运用的是dispatch_queue_create
函数 :
dispatch_queue_create
函数的回来值是一个行列。两个参数如下 :
const char *_Nullable label
:label是行列的称号,字符型,能够为空。是行列的唯一标识符。
dispatch_queue_attr_t _Nullable attr
:attr是行列的类型。一般有两个选择 :
(1).
DISPATCH_QUEUE_SERIAL
: 串行行列。
DISPATCH_QUEUE_SERIAL
是一个宏,自身是NULL,所以创立串行行列的时分,dispatch_queue_create
函数的第二个参数能够直接传NULL,就代表创立了一个串行行列。(2).
DISPATCH_QUEUE_CONCURRENT
: 并发行列。宏定义是
DISPATCH_GLOBAL_OBJECT(dispatch_queue_attr_t, _dispatch_queue_attr_concurrent)
举例 :
// 创立一个串行行列
dispatch_queue_t jd_serial_queue = dispatch_queue_create("jd_serial_queue", DISPATCH_QUEUE_SERIAL);
// 创立一个并发行列
dispatch_queue_t jd_concurrent_queue = dispatch_queue_create("jd_concurrent_queue", DISPATCH_QUEUE_CONCURRENT);
2. GCD供给的行列
GCD供给了两个特其他行列 : 主行列和大局并发行列。
- 主行列 :
获取方法 :
dispatch_get_main_queue()
特色 : 一切加入到主行列中的使命都会在主线程中履行。主行列相当所以一个特其他串行行列。
- 大局并发行列 :
获取方法 :
dispatch_get_global_queue(intptr_t identifier, uintptr_t flags);
特色 : 大局并发行列是一个特其他并发行列。是默许创立的并发行列,假如没有特其他需求,能够直接运用大局并发行列履行一些使命。
关于大局并发行列,两个参数的含义 :
(1).
identifier
: 大局并发行列的优先级,能够选择的宏如下 :
DISPATCH_QUEUE_PRIORITY_HIGH
: 高优先级。
DISPATCH_QUEUE_PRIORITY_DEFAULT
: 默许优先级。
DISPATCH_QUEUE_PRIORITY_LOW
: 低优先级。
DISPATCH_QUEUE_PRIORITY_BACKGROUND
: 后台运行行列。(2).
flags
: 需求传0,是苹果留着以后用的。现在便是传0,假如不传0,可能会回来NULL。
举例 :
// 获取主行列
dispatch_queue_t jd_main_queue = dispatch_get_main_queue();
// 获取大局并发行列
dispatch_queue_t jd_global_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
3. GCD的使命和函数的创立
使命 :
// GCD中的使命
dispatch_block_t mission_block = {
NSLog(@"nn这是一个GCD的使命,当时线程 : %@n",[NSThread currentThread]);
};
函数 :
dispatch_async(jd_concurrent_queue, ^{
NSLog(@"这是一个异步函数+一个并发行列,当时线程是 : %@",[NSThread currentThread]);
});
dispatch_sync(jd_serial_queue, ^{
NSLog(@"这是一个同步函数+一个串行行列,当时线程是 : %@",[NSThread currentThread]);
});
三、GCD的函数和行列合作
永远要清晰一点,使命(你要做的逻辑)都是放在行列里边的,函数是用来告诉行列怎样履行使命的。不论是同步函数还是异步函数,这两个函数也是使命。
1. 简略的无嵌套
区别 | 串行行列 | 并发行列 | 主行列 |
---|---|---|---|
同步函数 | 不敞开新线程, 按次序履行行列中的使命 |
不敞开新线程, 按次序履行行列中的使命 |
主线程中履行会形成死锁,不履行使命。 其他线程履行则不会出错。 |
异步函数 | 敞开1条新线程, 按次序履行行列中的使命 |
按状况敞开新线程, 并发履行行列中的使命 |
不敞开新线程, 按次序履行使命。 |
举例 :
1. 串行行列 + 同步函数 :
代码 :
履行成果 :
串行行列 + 同步函数 : 不敞开新的线程,按次序履行行列中的使命。
2. 串行行列 + 异步函数
代码 :
履行成果 :
串行行列 + 异步函数 : 敞开一条新的线程,按次序履行行列中的使命。
3. 并发行列 + 同步函数
代码 :
履行成果 :
并发行列 + 同步函数 : 不敞开新的线程,按次序履行行列中的使命。
4. 并发行列 + 异步函数
代码 :
履行成果 :
并发行列 + 异步函数 : 按状况敞开新的线程,使命履行次序不一定。
5. 主行列 + 同步函数
代码 :
履行成果 :
主行列 + 同步函数 : 形成死锁。由于同步函数也是一个使命,假设同步函数自身这个使命叫使命A,代码中的mission3(也能够是1或者2)叫使命B。在主行列中,使命A是在使命B的前面,所以先开端履行使命A,使命A中要求同步函数履行使命B,同步函数就会导致使命B不履行完结,其他使命(比方使命A)是不能够履行的,可是使命B在主行列中是在使命A的后边,主行列是不允许使命B先履行完结再履行使命A的,主行列和同步函数打架,一个要先完结使命A,一个由于已经开端履行使命B,必需要先完结使命B才能够给你主行列完结使命A,就形成了死锁。
6. 主行列 + 异步函数
代码 :
履行成果 :
主行列 + 异步函数 : 不敞开新线程,按次序履行使命。
2. 函数嵌套
异步函数 + 并发行列 嵌套(用同一个并发行列) |
同步函数 + 并发行列 嵌套(用同一个并发行列) |
异步函数 + 串行行列 嵌套(用同一个串行行列) |
同步函数 + 串行行列 嵌套(用同一个串行行列) |
|
---|---|---|---|---|
同步 | 异步可能敞开一个新线程, 嵌套的同步不会敞开新线程,在异步敞开的新线程中 串行履行使命 |
不敞开新线程, 串行履行使命 |
形成死锁 | 形成死锁 |
异步 | 敞开新线程, 嵌套的异步函数也会敞开新线程, 并发履行使命 |
同步函数不会敞开新线程, 异步函数会敞开新线程, 并发履行使命 |
异步函数会敞开新线程, 串行履行使命 |
异步函数会敞开新线程, 串行履行使命 |
举例
1. 异步函数嵌套同步函数 + 同一个并发行列
代码 :
履行成果 :
异步函数嵌套同步函数 + 同一个并发行列 : 异步函数可能会敞开新的线程,嵌套的同步函数不会敞开新的线程,会在异步函数敞开的新线程中串行的履行使命。不同的异步函数可能会敞开不同的新线程,不同的异步函数之间,使命的履行是并发的,可是同一个异步函数中嵌套的同步函数,一定是串行的。
2. 异步函数嵌套异步函数 + 同一个并发行列
代码 :
履行成果 :
异步函数自身就会敞开新的线程,嵌套的异步函数也可能会敞开新的线程,所以使命的履行完全是并发的,没有任何的次序可言。
3. 同步函数嵌套同步函数 + 同一个并发行列
代码 :
履行成果 :
同步函数自身是不敞开新线程的,不论是不是并发行列。由于同步函数自身就有一个特性:同步函数增加到行列的使命,同步函数会确保这个使命履行完结之后,才允许在同步函数之后增加的使命履行,又由所以同步嵌套同步,就导致了哪怕是并发行列,也是串行履行使命。
4. 同步函数嵌套异步函数 + 同一个并发行列
代码 :
履行成果 :
同步函数会确保自己增加的到并发行列的使命完结之后,再让其他的使命履行,同步函数嵌套异步函数,被嵌套的异步函数会单独敞开一条线程履行使命。可是一定要等前一个同步函数履行完结,下一个同步函数才能够履行使命。包括下一个同步函数中嵌套的异步函数,也要等到前一个同步函数履行完结使命才能够履行。
5. 异步函数嵌套同步函数 + 同一个串行行列
代码 :
履行成果 :
异步函数嵌套同步函数 + 同一个串行行列会形成死锁。
死锁原因 : 和主行列 + 同步函数一样。由于主行列自身也便是一个特其他串行行列,关于串行行列来说,一切的使命都是要依照次序履行的。异步函数先敞开了线程履行串行行列中的使命,串行行列中的第1个使命便是NSLog使命4,第2个使命是嵌套同步函数,第3个使命是这个嵌套的同步函数加到串行行列中的mission3,第4个使命是NSLog使命5。当第1个使命履行完结之后,要履行串行行列中的第2个使命,也便是同步函数给串行行列增加mission3,而且由所以同步函数,所以同步函数增加完使命mission3之后,就要履行这个使命mission3,不履行完结的话同步函数是不会让第4个使命
NSLog使命5
履行的,可是同步函数增加的使命mission3在串行行列中是排在同步函数自身这个使命之后的,也便是说,串行行列要求先履行完同步函数这个使命,才给你履行mission3,可是同步函数不同意,同步函数说 : 我履行完结的规范便是完结mission3,串行行列说 : mission3在你自己这个使命后边,不能够比你先履行。所以串行行列和同步函数打架,就形成了死锁。
死锁原因图 :
6. 异步函数嵌套异步函数 + 同一个串行行列
代码 :
履行成果 :
异步函数嵌套异步函数 + 同一个串行行列 : 会由所以串行行列的原因,导致增加到行列的使命串行履行,又由所以异步函数,所以会敞开一条新的线程。
为什么嵌套的异步函数不再敞开一条新的线程呢?由所以串行行列,就算敞开新的线程,仍然要根据串行行列的特性,一个一个的履行使命。不能够在前面的使命履行完结之前就履行后边的使命,所以嵌套的异步函数再敞开一条新的线程是没有意义的。
为什么嵌套的异步函数增加的使命是在最后才履行的?由于异步函数自身这个使命是在主线程中履行的,异步函数又不会等到使命履行结束后才结束,异步函数只管敞开新线程,并给串行行列增加部队,新线程什么时分履行串行行列中的使命,关于异步函数来说是不论的。那么主线程就不会直接去履行被嵌套的异步函数增加到串行行列的使命,而是只履行主行列中的使命,也便是外部的三个异步函数给串行行列增加使命。被嵌套的异步函数敞开线程又是一个耗时的操作,所以它抢不到新开的线程,只能等候新线程先把由主线程增加到串行行列的使命先履行完,再履行被嵌套的异步函数增加到串行行列的使命。
7. 同步函数嵌套异步函数 + 同一个串行行列
代码 :
履行成果 :
形成死锁。
同步函数嵌套同步函数 + 同一个串行行列 : 会形成死锁。
死锁的原因 : 同步函数是不会敞开新的线程的,所以不论是主行列还是自己创立的串行行列中的使命都是由主线程履行的。由所以串行行列,所以使命有必要是依照次序来履行的,前面的使命不完结,后边的使命是不能够履行的。主线程在履行同步函数这个使命的时分,同步函数自身这个使命便是给串行行列增加使命,并要求线程(同步函数没才能敞开新线程,又不是被异步函数包裹,所以只能由主线程来履行使命)把这些使命都履行完,才能够履行下面的使命。当主线程开端履行同步函数自身这个使命的时分,就被同步函数要求去履行串行行列中增加的使命,所以主线程就开端履行串行行列中的使命,而不是继续履行主行列中的使命。履行完NSLog使命4,开端履行被嵌套的同步函数,这时分,被嵌套的同步函数又要求主线程要把我的mission3履行完,才能够履行其他使命,可是mission3是增加到串行行列中的,串行行列中,被嵌套的同步函数自己这个使命是在mission3之前的,所以被嵌套的同步函数没有履行完结之前,串行行列是不允许mission3履行的,可是被嵌套的同步函数想要履行完结,就有必要完结mission3才算履行完结。所以又形成了被嵌套的同步函数和mission3互相等候,形成了死锁。和上面的两个原因是一样的。
8. 同步函数嵌套异步函数 + 同一个串行行列
代码 :
履行成果 :
同步函数嵌套异步函数 + 同一个串行行列 : 由所以同步函数在外,所以同步函数不会敞开新的线程,使命就要由主线程来履行,而且由所以同步函数,所以主线程只能先履行完一个同步函数,才能够履行下一个同步函数。被嵌套的异步函数能够敞开线程,可是这是一个耗时操作,所以它增加使命(例如mission3)的速度要比NSLog使命5慢。所以每个同步函数中的第三个NSLog使命都比异步函数增加使命(例如mission3)到串行行列要早。串行行列会依照增加使命的次序履行使命,被嵌套的异步函数会敞开新的线程履行使命。
总结
- GCD是苹果推出的多线程处理方案,是由C编写的函数。
- GCD能够自己办理线程的生命周期,例如 : 线程的创立、线程的调度、线程的堵塞、线程的毁掉。
- GCD能够调用CPU的多中心。
- GCD的中心是使命、行列和函数。能够说 GCD = 使命 + 行列(寄存使命的) + 函数(履行使命的),将使命放入到行列也是一种使命,所以函数自身也是一种使命。
- GCD中的行列分为 : 串行行列和并发行列。其中有两个特其他行列,一个是主行列,一个是大局并发行列。
- 串行行列 : 串行行列中的使命有必要一个接一个的履行,遵从FIFO准则,先进先出。
- 主行列 : 主行列是一种特其他串行行列,特别在主行列中的使命全部在主线程中履行。
- 并发行列 : 并发行列中的使命是一起履行的,谁先履行完结,谁就先输出。履行完次序不一定。可是并发行列只要在异步函数中才表现才能,由于同步函数不敞开新的线程,所以假如用同步函数,实际上使命还是先进先出的,谁先进来,线程就先履行谁。
- 大局并发行列 : 大局并发行列是一种特其他并发行列,是GCD供给的默许并发行列,想运用并发行列,又没有特其他需求,能够直接就用大局并发行列,大局并发行列能够设置优先级。
- 同步函数 : 同步函数不具有拓荒新线程的才能,同步函数能够增加使命到指定行列中,在同步函数增加的使命没有履行完结之前,同步函数运用的这条线程是不能够履行其他的使命的,其他的使命都要处于等候状况中,直到同步函数增加的使命履行完结,其他的使命才能够履行。
- 异步函数 : 异步函数具有拓荒新线程的才能,异步函数也能够增加使命到指定的行列中,可是异步函数只管增加使命和履行使命,不会确保增加的使命一定要立刻履行完结,能够等候其他的使命先履行,不会影响到当时线程中其他使命的履行。
- 会形成死锁的状况 : 一般都是串行行列 + 同步函数,由于串行行列遵从FIFO,先进的必先出。同步函数又有必要确保自己增加的使命履行完结才能够履行其他的。
(1). 主行列 + 同步函数
(2). 异步函数嵌套同步函数 + 串行行列。
(3). 同步函数嵌套同步函数 + 串行行列。