GCD简介
- 什么是GCD?
全称是 Grand Central Dispatch
,纯 C
语言,提供了非常强大的函数
2、GCD 的优势
- GCD 是苹果公司为多核的并行运算提出的解决方案
- GCD 会自动利用更多的
CPU
内核(比如双核、四核)
- GCD 会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
- 程序员只需要告诉
GCD
想要执行什么任务,不需要编写任何线程管理代码
【重点】用一句话总结 GCD : 将任务添加到队列,并且指定执行任务的函数。
GCD 核心
在日常开发中,GCD一般写成下面的形式
1 2 3
| diaptch_async(dispatch_queue_create("com.zj.Queue",NULL)), ^{ NSLog(@"GCD基本使用") });
|
将上述代码拆分,方便我们来理解 GCD
的 核心
,主要是由 任务 + 队列 + 函数
构成
1 2 3 4 5 6 7 8 9
| //*****GCD基础写法***** // 创建任务 dispatch_block_t block= ^{ NSLog(@"hello GCD"); } // 创建队列 dispatch_queue_t queue = dispatch_queue_create("com.zj.Queue",NULL); // 将任务添加到队列,并指定函数执行 dispatch_async(queue,block);
|
- dispatch_block_t:创建任务
- dispatch_queue_t:创建队列
- dispatch_async:函数
函数和队列
1、在GCD中函数按照执行方式分为 同步函数
和 异步函数
同步函数:dispatch_sync
- 必须等待当前语句执行完毕,才会执行下一条语句
- 不会开启线程,即不具备开启新线程的能力
- 在当前执行
block
任务
异步函数:dispatch_async
- 会开启新的线程执行
block
任务
- 异步是多线程的代名词
综上所述,两种执行方式的 主要区别
有两点:
是否等待
队列的任务执行完毕
是否具备开启新线程
的能力
2、队列
在GCD中队列是用来 存放任务
,是一种特殊的 线性表
,遵循 FIFO(先进先出)
原则,新任务总是被 插入到队尾
,而任务的读取是从 队首开始读取
,每读一个任务,则队列中释放一个任务,如下图所示:
队列主要分为 串行队列(Serial Dispatch Queue)
和 并发队列(Concurrent Dispatch Queue)
两种,如下图所示
1 2 3 4
| // 串行队列的获取方法 dispatch_queue_t serialQueue1 = dispatch_queue_create("com.ZJ.Queue",DISPATCH_QUQUQ_SERIAL);
dispatch_quque_t serialQuquq2 = dispatch_ququq_create("com.ZJ.Queue",Null);
|
1 2
| // 并发队列的获取方法 dispatch_quque_t concurrentQueue = dispatch_quque_create("com.ZJ.Queue",DISPATCH_QUQUE_CONCURRENT);
|
主队列 和 全局并发队列
在GCD中,针对这两种队列,分别提供了 主队列(Main Dispatch Queue)
和 全局并发队列(Global Dispatch Queue)
1 2
| // 主队列的获取方法 dispatch_queue_t mainQueue = dispatch_get_main_queue();
|
1 2 3 4 5 6 7 8
| // 全集并发队列的获取方法 dispatch_queue_t globalQueue = dispatch_get_global_queue(0,0);
// 优先级从高到底(对应的服务质量)依次为 - DISPATCH_QUEUE_PRIORITY_HIGH: QOS_CLASS_USER_INITIATED - DISPATCH_QUEUE_PRIORITY_DEFAULT: QOS_CLASS_DEFAULT - DISPATCH_QUEUE_PRIORITY_LOW: QOS_CLASS_UTILITY - DISPATCH_QUEUE_PRIORITY_BACKGROUND: QOS_CLASS_BACKGROUND
|
全局并发队列 + 主队列 配合使用
在日常开发中, 全局队列 + 并发队列
一般是这样配合使用的
1 2 3 4 5 6 7
| // 主队列 + 全局并发队列 的日常使用 dispatch_async(dispatch_get_global_queue(0,0), ^{ // 执行耗时操作 dispatch_async(dispatch_get_main_queue(),^{ // 回到主线程进行UI操作 }); });
|
函数与队列的不同组合
- 串行队列 + 同步函数
【任务 按顺序执行
】:任务一个接一个的在当前线程执行,不会开辟新线程
1 2 3 4 5 6 7 8 9 10
| // 串行队列 + 同步函数 - (void)test1{ NSLog(@"串行 + 同步:%@",[NSThread currentThread]); dispatch_queue_t serialQueue = dispatch_queue_create("com.ZJ.Queue", DISPATCH_QUEUE_SERIAL); for (int i = 0; i < 5; i ++) { dispatch_sync(serialQueue, ^{ NSLog(@"串行 + 同步:%d - %@",i,[NSThread currentThread]); }); } }
|
运行代码,查看打印结果如下
1 2 3 4 5 6
| 2022-01-12 15:13:39.595134+0800 GCD之函数和队列[4500:155055] 串行 + 同步:<_NSMainThread: 0x6000025d00c0>{number = 1, name = main} 2022-01-12 15:13:39.595275+0800 GCD之函数和队列[4500:155055] 串行 + 同步:0 - <_NSMainThread: 0x6000025d00c0>{number = 1, name = main} 2022-01-12 15:13:39.595388+0800 GCD之函数和队列[4500:155055] 串行 + 同步:1 - <_NSMainThread: 0x6000025d00c0>{number = 1, name = main} 2022-01-12 15:13:39.595512+0800 GCD之函数和队列[4500:155055] 串行 + 同步:2 - <_NSMainThread: 0x6000025d00c0>{number = 1, name = main} 2022-01-12 15:13:39.595639+0800 GCD之函数和队列[4500:155055] 串行 + 同步:3 - <_NSMainThread: 0x6000025d00c0>{number = 1, name = main} 2022-01-12 15:13:39.595759+0800 GCD之函数和队列[4500:155055] 串行 + 同步:4 - <_NSMainThread: 0x6000025d00c0>{number = 1, name = main}
|
- 串行队列 + 异步函数
【任务 按顺序执行
】:任务一个接一个的执行,会 开辟新线程
1 2 3 4 5 6 7 8 9 10
| // 串行队列 + 异步函数 - (void)test2{ NSLog(@"串行 + 异步:%@",[NSThread currentThread]); dispatch_queue_t serialQueue = dispatch_queue_create("com.ZJ.Queue", NULL); for (int i = 0; i < 5; i ++) { dispatch_async(serialQueue, ^{ NSLog(@"串行 + 异步:%d - %@",i,[NSThread currentThread]); }); } }
|
运行代码,查看打印结果如下
1 2 3 4 5 6
| 2022-01-12 15:20:12.884840+0800 GCD之函数和队列[4590:160591] 串行 + 异步:<_NSMainThread: 0x600000ee4140>{number = 1, name = main} 2022-01-12 15:20:12.885010+0800 GCD之函数和队列[4590:160734] 串行 + 异步:0 - <NSThread: 0x600000eb01c0>{number = 3, name = (null)} 2022-01-12 15:20:12.885136+0800 GCD之函数和队列[4590:160734] 串行 + 异步:1 - <NSThread: 0x600000eb01c0>{number = 3, name = (null)} 2022-01-12 15:20:12.885262+0800 GCD之函数和队列[4590:160734] 串行 + 异步:2 - <NSThread: 0x600000eb01c0>{number = 3, name = (null)} 2022-01-12 15:20:12.885392+0800 GCD之函数和队列[4590:160734] 串行 + 异步:3 - <NSThread: 0x600000eb01c0>{number = 3, name = (null)} 2022-01-12 15:20:12.885538+0800 GCD之函数和队列[4590:160734] 串行 + 异步:4 - <NSThread: 0x600000eb01c0>{number = 3, name = (null)}
|
- 并发队列 + 同步函数
【任务 按顺序执行
】:任务一个接一个的执行,不开辟新线程
1 2 3 4 5 6 7 8 9 10
| // 并发队列 + 同步函数 - (void)test3{ NSLog(@"并发 + 同步:%@",[NSThread currentThread]); dispatch_queue_t concurrentQueue = dispatch_queue_create("com.ZJ.Queue", DISPATCH_QUEUE_CONCURRENT); for (int i = 0; i < 5; i ++) { dispatch_sync(serialQueue, ^{ NSLog(@"并发 + 同步:%d - %@",i,[NSThread currentThread]); }); } }
|
运行代码,查看打印结果如下
1 2 3 4 5 6
| 22-01-12 15:23:15.939687+0800 GCD之函数和队列[4630:163688] 并发 + 同步:<_NSMainThread: 0x600000de8000>{number = 1, name = main} 2022-01-12 15:23:15.939830+0800 GCD之函数和队列[4630:163688] 并发 + 同步:0 - <_NSMainThread: 0x600000de8000>{number = 1, name = main} 2022-01-12 15:23:15.939922+0800 GCD之函数和队列[4630:163688] 并发 + 同步:1 - <_NSMainThread: 0x600000de8000>{number = 1, name = main} 2022-01-12 15:23:15.940014+0800 GCD之函数和队列[4630:163688] 并发 + 同步:2 - <_NSMainThread: 0x600000de8000>{number = 1, name = main} 2022-01-12 15:23:15.940102+0800 GCD之函数和队列[4630:163688] 并发 + 同步:3 - <_NSMainThread: 0x600000de8000>{number = 1, name = main} 2022-01-12 15:23:15.940217+0800 GCD之函数和队列[4630:163688] 并发 + 同步:4 - <_NSMainThread: 0x600000de8000>{number = 1, name = main}
|
- 并发队列 + 异步函数
【任务 乱序执行
】:任务执行无顺序,会 开辟新线程
1 2 3 4 5 6 7 8 9 10
| // 并发队列 + 异步函数 - (void)test4{ NSLog(@"并发 + 异步:%@",[NSThread currentThread]); dispatch_queue_t concurrentQueue = dispatch_queue_create("com.ZJ.Queue", DISPATCH_QUEUE_CONCURRENT); for (int i = 0; i < 5; i ++) { dispatch_async(serialQueue, ^{ NSLog(@"并发 + 异步:%d - %@",i,[NSThread currentThread]); }); } }
|
运行代码,查看打印结果如下
1 2 3 4 5 6
| 2022-01-12 15:26:35.249949+0800 GCD之函数和队列[4716:166604] 并发 + 异步:<_NSMainThread: 0x600003344740>{number = 1, name = main} 2022-01-12 15:26:35.250144+0800 GCD之函数和队列[4716:166729] 并发 + 异步:0 - <NSThread: 0x60000332c100>{number = 7, name = (null)} 2022-01-12 15:26:35.250153+0800 GCD之函数和队列[4716:166735] 并发 + 异步:1 - <NSThread: 0x600003300d40>{number = 6, name = (null)} 2022-01-12 15:26:35.250174+0800 GCD之函数和队列[4716:166734] 并发 + 异步:3 - <NSThread: 0x60000330f0c0>{number = 4, name = (null)} 2022-01-12 15:26:35.250238+0800 GCD之函数和队列[4716:166731] 并发 + 异步:4 - <NSThread: 0x600003328040>{number = 8, name = (null)} 2022-01-12 15:26:35.250277+0800 GCD之函数和队列[4716:166733] 并发 + 异步:2 - <NSThread: 0x600003305c40>{number = 3, name = (null)}
|
- 主队列 + 同步函数
【造成 死锁
】:任务 相互等待
,造成 死锁
造成死锁的原因分析如下:
- 主队列有两个任务,顺序为:
NSLog任务 - 同步block
- 执行NSLog任务后,执行同步的block,会将
任务1(即 i=1)
加入到主队列,主队列顺序为 :NSLog任务 - 同步block - 任务1
任务1
的执行需要 等待同步block执行完毕
才会执行,而 同步block
的执行需要 等待任务1执行完毕
,所以就造成了 互相等待
的情况,即造成 死锁崩溃
。
【死锁现象】
主线程
因为你 同步函数
的原因 等着先执行任务
主队列等着主线程的任务
执行完毕再执行自己的任务
主队列和主线程相互等待
会造成死锁
- 主队列 + 异步函数
【任务 按顺序执行
】:任务一个接一个的执行,不开辟线程
1 2 3 4 5 6 7 8 9 10
| // 主队列 + 异步函数 - (void)test6{ NSLog(@"主队列 + 异步:%@",[NSThread currentThread]); dispatch_queue_t mainQueue = dispatch_get_main_queue(); for (int i = 0; i < 5; i ++) { dispatch_async(mainQueue, ^{ NSLog(@"主队列 + 异步:%d - %@",i,[NSThread currentThread]); }); } }
|
运行代码,查看打印结果如下
1 2 3 4 5 6
| 2022-01-12 16:27:53.197940+0800 GCD之函数和队列[5926:204672] 主队列 + 异步:<_NSMainThread: 0x600001880000>{number = 1, name = main} 2022-01-12 16:27:53.213528+0800 GCD之函数和队列[5926:204672] 主队列 + 异步:0 - <_NSMainThread: 0x600001880000>{number = 1, name = main} 2022-01-12 16:27:53.213718+0800 GCD之函数和队列[5926:204672] 主队列 + 异步:1 - <_NSMainThread: 0x600001880000>{number = 1, name = main} 2022-01-12 16:27:53.213879+0800 GCD之函数和队列[5926:204672] 主队列 + 异步:2 - <_NSMainThread: 0x600001880000>{number = 1, name = main} 2022-01-12 16:27:53.214016+0800 GCD之函数和队列[5926:204672] 主队列 + 异步:3 - <_NSMainThread: 0x600001880000>{number = 1, name = main} 2022-01-12 16:27:53.214196+0800 GCD之函数和队列[5926:204672] 主队列 + 异步:4 - <_NSMainThread: 0x600001880000>{number = 1, name = main}
|
- 全局并发队列 + 同步函数
【任务 按顺序执行
】:任务一个接一个的执行,不开辟新线程
1 2 3 4 5 6 7 8 9 10
| // 全局并发队列 + 同步函数 - (void)test7{ NSLog(@"全局并发 + 同步:%@",[NSThread currentThread]); dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0); for (int i = 0; i < 5; i ++) { dispatch_sync(globalQueue, ^{ NSLog(@"全局并发 + 同步:%d - %@",i,[NSThread currentThread]); }); } }
|
运行代码,查看打印结果如下
1 2 3 4 5 6
| 2022-01-12 16:32:08.207844+0800 GCD之函数和队列[6000:209221] 全局并发 + 同步:<_NSMainThread: 0x60000031c140>{number = 1, name = main} 2022-01-12 16:32:08.207992+0800 GCD之函数和队列[6000:209221] 全局并发 + 同步:0 - <_NSMainThread: 0x60000031c140>{number = 1, name = main} 2022-01-12 16:32:08.208098+0800 GCD之函数和队列[6000:209221] 全局并发 + 同步:1 - <_NSMainThread: 0x60000031c140>{number = 1, name = main} 2022-01-12 16:32:08.208280+0800 GCD之函数和队列[6000:209221] 全局并发 + 同步:2 - <_NSMainThread: 0x60000031c140>{number = 1, name = main} 2022-01-12 16:32:08.208400+0800 GCD之函数和队列[6000:209221] 全局并发 + 同步:3 - <_NSMainThread: 0x60000031c140>{number = 1, name = main} 2022-01-12 16:32:08.208504+0800 GCD之函数和队列[6000:209221] 全局并发 + 同步:4 - <_NSMainThread: 0x60000031c140>{number = 1, name = main}
|
- 全局并发队列 + 异步函数
【任务 乱序执行
】:任务乱序执行,会开辟新线程
1 2 3 4 5 6 7 8 9 10
| // 全局并发队列 + 异步函数 - (void)test8{ NSLog(@"全局并发 + 异步:%@",[NSThread currentThread]); dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0); for (int i = 0; i < 5; i ++) { dispatch_async(globalQueue, ^{ NSLog(@"全局并发 + 异步:%d - %@",i,[NSThread currentThread]); }); } }
|
运行代码,查看打印结果如下
1 2 3 4 5 6
| 2022-01-12 16:34:18.147614+0800 GCD之函数和队列[6082:211916] 全局并发 + 异步:<_NSMainThread: 0x600002cd8340>{number = 1, name = main} 2022-01-12 16:34:18.147839+0800 GCD之函数和队列[6082:212058] 全局并发 + 异步:1 - <NSThread: 0x600002cdcdc0>{number = 4, name = (null)} 2022-01-12 16:34:18.147859+0800 GCD之函数和队列[6082:212057] 全局并发 + 异步:0 - <NSThread: 0x600002c97640>{number = 3, name = (null)} 2022-01-12 16:34:18.147880+0800 GCD之函数和队列[6082:212052] 全局并发 + 异步:2 - <NSThread: 0x600002cb9140>{number = 7, name = (null)} 2022-01-12 16:34:18.147880+0800 GCD之函数和队列[6082:212053] 全局并发 + 异步:3 - <NSThread: 0x600002c972c0>{number = 5, name = (null)} 2022-01-12 16:34:18.147892+0800 GCD之函数和队列[6082:212055] 全局并发 + 异步:4 - <NSThread: 0x600002c80000>{number = 8, name = (null)}
|
总结
函数\队列 |
串行队列 |
并发队列 |
主队列 |
全局并发队列 |
同步函数 |
顺序执行,不开辟新线程 |
顺序执行,不开辟新线程 |
死锁 |
顺序执行,不开辟新线程 |
异步函数 |
顺序执行,开辟新线程 |
乱序执行,开辟新线程 |
顺序执行,不开破新线程 |
乱序执行,开辟先线程 |
相关面试题解析
- 【面试题-1】:并行队列 + 异步函数
下面代码的输出顺序是什么?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| - (void)interview01{ // 并发队列 dispatch_queue_t queue = dispatch_queue_create("com.ZJ.Queue", DISPATCH_QUEUE_CONCURRENT); NSLog(@"1 %@",[NSThread currentThread]); // 耗时 dispatch_async(queue, ^{ NSLog(@"2 %@",[NSThread currentThread]); // 耗时 dispatch_async(queue, ^{ NSLog(@"3 %@",[NSThread currentThread]); }); NSLog(@"4 %@",[NSThread currentThread]); }); NSLog(@"5 %@",[NSThread currentThread]); }
|
运行代码,查看打印结果如下
1 2 3 4 5
| 2022-01-12 16:50:01.867461+0800 GCD之函数和队列[6319:225209] 1 <_NSMainThread: 0x6000023f4840>{number = 1, name = main} 2022-01-12 16:50:01.867626+0800 GCD之函数和队列[6319:225209] 5 <_NSMainThread: 0x6000023f4840>{number = 1, name = main} 2022-01-12 16:50:01.867695+0800 GCD之函数和队列[6319:225342] 2 <NSThread: 0x600002399ec0>{number = 6, name = (null)} 2022-01-12 16:50:01.867840+0800 GCD之函数和队列[6319:225342] 4 <NSThread: 0x600002399ec0>{number = 6, name = (null)} 2022-01-12 16:50:01.867852+0800 GCD之函数和队列[6319:225345] 3 <NSThread: 0x6000023a8b00>{number = 4, name = (null)}
|
异步函数
并 不会阻塞
主队列,会开辟新线程
执行异步任务
代码分析:如下图所示,红线表示任务的执行顺序
主线程
的任务队列为:任务1、异步block1、任务5
,其中 异步block1
会比较耗费性能,任务1
和 任务5
的任务复杂度是相同的,所以 任务1
和 任务5
优先于 异步block1
执行
- 在
异步block1
中,任务队列为:任务2、异步block2、任务4
,其中 block2
相对比较耗费性能,任务2
和 任务4
复杂度一样,所以 任务2
和 任务4
优先于 block2
执行
- 最后执行
block2
中的 任务3
- 在极端情况下,可能会出现
任务2
优先 任务1
和 任务5
执行,原因是出现了当前主线程卡顿或者延迟
的情况
代码修改
- 【修改1】:将
并行队列
改成 串行队列
,对结果没有任何影响,顺序仍然是 15243
- 【修改2】:在
任务5
之前,休眠2s,即 sleep(2)
,执行的顺序为:12435
,原因是因为 I/O 的打印,相比于休眠 2s
,复杂度更简单,所以 异步block1
会优先于任务5执行。当然如果主队列阻塞,会出现其他的执行顺序。
- 【面试题-2】:并发队列 + 异步函数嵌套同步函数
下面代码的输出顺序是什么?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| - (void)interview2{ // 并发队列 dispatch_queue_t queue = dispatch_queue_create("com.ZJ.Queue", DISPATCH_QUEUE_CONCURRENT); NSLog(@"1 %@",[NSThread currentThread]); //异步函数 dispatch_async(queue, ^{ NSLog(@"2 %@",[NSThread currentThread]); // 同步函数 dispatch_sync(queue, ^{ NSLog(@"3 %@",[NSThread currentThread]); }); NSLog(@"4 %@",[NSThread currentThread]); }); NSLog(@"5 %@",[NSThread currentThread]); }
|
运行代码,查看打印结果如下
1 2 3 4 5
| 2022-01-12 17:37:11.034204+0800 GCD之函数和队列[7126:263322] 1 <_NSMainThread: 0x600000074300>{number = 1, name = main} 2022-01-12 17:37:11.034340+0800 GCD之函数和队列[7126:263322] 5 <_NSMainThread: 0x600000074300>{number = 1, name = main} 2022-01-12 17:37:11.034378+0800 GCD之函数和队列[7126:263438] 2 <NSThread: 0x600000039c40>{number = 3, name = (null)} 2022-01-12 17:37:11.034494+0800 GCD之函数和队列[7126:263438] 3 <NSThread: 0x600000039c40>{number = 3, name = (null)} 2022-01-12 17:37:11.034625+0800 GCD之函数和队列[7126:263438] 4 <NSThread: 0x600000039c40>{number = 3, name = (null)}
|
代码分析:
任务1、block1、任务5
的分析和前面一致,执行顺序为:任务1 任务5 block1
- 在
异步block1
中,任务2、block2、任务4
的执行顺序,由于 block2 是同步函数会阻塞当前线程,所以 任务4
需要等待 block2
中的 任务3
执行完成后,才能执行,所以异步block中的执行顺序是:任务2、任务3、任务4
- 【面试题-3】:串行队列 + 异步函数嵌套同步函数
下面代码的输出顺序是什么?会出现什么情况?为什么?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| - (void)interview3{ // 串行队列 dispatch_queue_t queue = dispatch_queue_create("com.ZJ.Queue", DISPATCH_QUEUE_SERIAL); NSLog(@"1 %@",[NSThread currentThread]); //异步函数 dispatch_async(queue, ^{ NSLog(@"2 %@",[NSThread currentThread]); // 同步函数 dispatch_sync(queue, ^{ NSLog(@"3 %@",[NSThread currentThread]); }); NSLog(@"4 %@",[NSThread currentThread]); }); NSLog(@"5 %@",[NSThread currentThread]); }
|
运行代码,查看打印结果如下
分析:
如下图所示,红色代表任务执行顺序,黄色线表示等待
- 首先,在主线程上有
任务1、异步block、任务5
,首先执行 任务1
,由于 异步block
复杂度高于 任务5
,因此,执行顺序是 任务1 -> 任务5 -> 异步block
- 在异步block中,开启了新的线程,执行顺序首先是
任务2
,然后是 同步block
,同步函数会阻塞当前线程
,所以执行 任务4
需要 等待任务3完成
,由于 任务3的执行
,需要 等待异步block完成
,异步block完成
需要 等待任务4完成
,
- 因此就会造成
任务4等待任务3,任务3等待任务4
,即 互相等待
的局面,就会造成 死锁
,这里有个重点是 关键的堆栈slow
修改
去掉任务4,执行顺序是什么?
- 还是会死锁,因为
任务3等待的是异步block执行完毕,而异步block等待任务3完成
4、【面试题4-新浪】:异步函数 + 同步函数 + 并发队列
下面代码执行的顺序是什么?(答案是AC)
A:1230789
B:1237890
C:3120798
D:2137890
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| - (void)interview04{ // 并发队列 dispatch_queue_t queue = dispatch_queue_create("com.ZJ.Queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{ // 耗时 NSLog(@"1 %@",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"2 %@",[NSThread currentThread]); }); // 同步 dispatch_sync(queue, ^{ NSLog(@"3 %@",[NSThread currentThread]); }); NSLog(@"0 %@",[NSThread currentThread]);
dispatch_async(queue, ^{ NSLog(@"7 %@",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"8 %@",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"9 %@",[NSThread currentThread]); }); }
|
打印结果是: (1、2、3无序) 0 (7、8、9无序),可以确定的 0 一定在 3 之后, 在789之前
分析:
任务1
和 任务2
是由于 异步函数 + 并发队列
,会开启线程,所以没有固定顺序
任务7、任务8、任务9
同理,会开启线程,所以没有固定顺序
任务3
是 同步函数 + 并发队列
,同步函数会 阻塞主线程
,但是也只会阻塞0,所以,可以确定的是 0一定在3之后,在789之前
以下是不同的打印结果:
1 2 3 4 5 6 7
| 2022-01-14 14:45:42.843292+0800 GCD之函数和队列[18838:775240] 3 <_NSMainThread: 0x600001198a00>{number = 1, name = main} 2022-01-14 14:45:42.843329+0800 GCD之函数和队列[18838:775341] 1 <NSThread: 0x600001189cc0>{number = 5, name = (null)} 2022-01-14 14:45:42.843361+0800 GCD之函数和队列[18838:775340] 2 <NSThread: 0x600001195a40>{number = 7, name = (null)} 2022-01-14 14:45:42.843415+0800 GCD之函数和队列[18838:775240] 0 <_NSMainThread: 0x600001198a00>{number = 1, name = main} 2022-01-14 14:45:42.843546+0800 GCD之函数和队列[18838:775340] 7 <NSThread: 0x600001195a40>{number = 7, name = (null)} 2022-01-14 14:45:42.843628+0800 GCD之函数和队列[18838:775341] 8 <NSThread: 0x600001189cc0>{number = 5, name = (null)} 2022-01-14 14:45:42.843653+0800 GCD之函数和队列[18838:775343] 9 <NSThread: 0x60000119ce80>{number = 6, name = (null)}
|
1 2 3 4 5 6 7 8
| 2022-01-14 14:50:18.831754+0800 GCD之函数和队列[18899:780910] 3 <_NSMainThread: 0x600000054a00>{number = 1, name = main} 2022-01-14 14:50:18.831835+0800 GCD之函数和队列[18899:781015] 2 <NSThread: 0x600000010e00>{number = 6, name = (null)} 2022-01-14 14:50:18.831801+0800 GCD之函数和队列[18899:781016] 1 <NSThread: 0x600000052340>{number = 5, name = (null)} 2022-01-14 14:50:18.831887+0800 GCD之函数和队列[18899:780910] 0 <_NSMainThread: 0x600000054a00>{number = 1, name = main} 2022-01-14 14:50:18.832053+0800 GCD之函数和队列[18899:781016] 7 <NSThread: 0x600000052340>{number = 5, name = (null)} 2022-01-14 14:50:18.832058+0800 GCD之函数和队列[18899:781015] 8 <NSThread: 0x600000010e00>{number = 6, name = (null)} 2022-01-14 14:50:18.832082+0800 GCD之函数和队列[18899:781019] 9 <NSThread: 0x600000011b80>{number = 3, name = (null)}
|
1 2 3 4 5 6 7
| 2022-01-14 14:51:47.433576+0800 GCD之函数和队列[18934:784384] 3 <_NSMainThread: 0x6000030280c0>{number = 1, name = main} 2022-01-14 14:51:47.433577+0800 GCD之函数和队列[18934:784516] 2 <NSThread: 0x6000030741c0>{number = 4, name = (null)} 2022-01-14 14:51:47.433578+0800 GCD之函数和队列[18934:784518] 1 <NSThread: 0x60000304d540>{number = 6, name = (null)} 2022-01-14 14:51:47.433718+0800 GCD之函数和队列[18934:784384] 0 <_NSMainThread: 0x6000030280c0>{number = 1, name = main} 2022-01-14 14:51:47.433880+0800 GCD之函数和队列[18934:784518] 7 <NSThread: 0x60000304d540>{number = 6, name = (null)} 2022-01-14 14:51:47.433890+0800 GCD之函数和队列[18934:784513] 9 <NSThread: 0x600003040000>{number = 5, name = (null)} 2022-01-14 14:51:47.433891+0800 GCD之函数和队列[18934:784516] 8 <NSThread: 0x6000030741c0>{number = 4, name = (null)}
|
5、【面试题-5-美团】:下面代码中,队列的类型有几种?
1 2 3 4 5 6 7 8 9 10 11
| // 串行队列 - Serial Dispatch Queue dispatch_queue_t serialQueue = dispatch_queue_create("com.ZJ.Queue", NULL); // 并发队列 - Concurrent Dispatch Queue dispatch_queue_t concurrentQueue = dispatch_queue_create("com.ZJ.Queue", DISPATCH_QUEUE_CONCURRENT); // 主队列 - Main Dispatch Queue dispatch_queue_t mainQueue = dispatch_get_main_queue(); // 全局并发队列 - Global Dispatch Queue dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
|
队列总共有两种:串行队列
和 并发队列
- 串行队列:serialQueue、mainQueue
- 并发队列:concurrentQueue、globalQueue