一、基本概念
1、什么是GCD
- 全称是Grand Central Dispatch,可译为“牛逼的中枢调度器”
- 纯C语言,提供了非常多强大的函数
2、GCD的优势
- GCD是苹果公司为多核的并行运算提出的解决方案
- GCD会自动利用更多的CPU内核(比如双核、四核)
- GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
- 程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码
3、任务和队列
(1)GCD中有2个核心概念
任务:执行什么操作
队列:用来存放任务
(2)GCD的使用就2个步骤
<1>定制任务:确定想做的事情
<2>将任务添加到队列中:
GCD会自动将队列中的任务取出,放到对应的线程中执行
任务的取出遵循队列的FIFO原则:先进先出,后进后出
二、执行任务
1、执行任务
(1)GCD中有2个用来执行任务的函数
用同步的方式执行任务
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
queue:队列
block:任务
用异步的方式执行任务
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
(2)同步和异步的区别
同步:在当前线程中执行
异步:在另一条线程中执行
2、队列的类型
GCD的队列可以分为2大类型
(1)并行队列(Concurrent Dispatch Queue)
可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
并发功能只有在异步(dispatch_async)函数下才有效
(2)串行队列(Serial Dispatch Queue)
让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)
3、比较容易混淆的术语:同步、异步、并发、串行
(1)同步和异步决定了要不要开启新的线程
同步:在当前线程中执行任务,不具备开启新线程的能力
异步:在新的线程中执行任务,具备开启新线程的能力
(2)并发和串行决定了任务的执行方式
并发:多个任务并发(同时)执行
串行:一个任务执行完毕后,再执行下一个任务
三、“串行队列”、“并行队列”、“全局队列”和“主队列”
DISPATCH_QUEUE_SERIAL 串行队列 与NULL等效
DISPATCH_QUEUE_CONCURRENT 并行队列
1、串行队列
(1)第一种:串行队列,异步任务
1 // 1.串行队列 2 // 在使用GCD的时候,先敲dispatch 3 // 在C语言中,定义对象通常是以 _t 或者 Ref 结尾的 4 // dispatch_queue_t q = dispatch_queue_create("ios", DISPATCH_QUEUE_SERIAL); 5 dispatch_queue_t q = dispatch_queue_create("ios", NULL); 6 7 NSLog(@"%@", [NSThread currentThread]); 8 9 // 2. 异步任务 async,能够开线程 10 // 串行队列中,异步任务最多只能开一条线程,所有任务顺序执行! 11 // 串行队列,异步任务,在多线程中,是斯坦福大学最推荐的一种多线程方式! 12 // 优点:将任务放在其他线程中工作,每个任务顺序执行,便于调试 13 // 缺点:并发能力不强,最多只能使用一条子线程! 14 for (int i = 0; i < 10; i++) { 15 dispatch_async(q, ^{ 16 NSLog(@"%@ - %d", [NSThread currentThread], i); 17 }); 18 }
输出结果:
结果说明:
① 异步任务 async 能够开线程
② 串行队列中,异步任务最多只能开一条线程,所有任务顺序执行!
③ 串行队列,异步任务,在多线程中,是斯坦福大学最推荐的一种多线程方式!
优点:将任务放在其它线程中工作,每个任务顺序执行,便于调试
缺点:并发能力不强,最多只能使用一条子线程!
(2)第二种:串行队列,同步任务
1 // 2. 同步任务 sync(没用处) 2 for (int i = 0; i < 10; i++) { 3 dispatch_sync(q, ^{ 4 NSLog(@"%@ - %d", [NSThread currentThread], i); 5 }); 6 }
输出结果:
从输出结果可以看出,串行队列,同步任务,是在主线程顺序执行的,默认就是顺序执行的,所以这个没什么用。
2、并行队列
(1)第一种:并行队列,异步任务
1 // 1.并行队列 2 dispatch_queue_t q = dispatch_queue_create("ios", DISPATCH_QUEUE_CONCURRENT); 3 4 NSLog(@"%@", [NSThread currentThread]); 5 6 // 2.异步任务 7 for (int i = 0; i < 10; i++) { 8 dispatch_async(q, ^{ 9 NSLog(@"%@ - %d", [NSThread currentThread], i); 10 }); 11 }
输出结果是:
① 并行队列,异步任务,开启了多条子线程执行。
② 异步任务,会在多条线程上工作,具体开多少条线程,由系统决定
③ 仍然是按照任务添加到队列中的顺序被调度,只是执行先后可能会有差异!
④ 能够将耗时的操作,放到子线程中工作
⑤ 与串行队列异步任务相比,并发性能更好!但是执行的先后顺序,不固定
(2)第二种,并行队列,同步任务
1 // 1.并行队列 2 dispatch_queue_t q = dispatch_queue_create("ios", DISPATCH_QUEUE_CONCURRENT); 3 4 NSLog(@"%@", [NSThread currentThread]); 5 6 // 2.同步任务 7 for (int i = 0; i < 10; i++) { 8 dispatch_sync(q, ^{ 9 NSLog(@"%@ - %d", [NSThread currentThread], i); 10 }); 11 }
输出结果:
跟串行队列,同步任务的输出结果是相同的。
3、全局队列
全局队列又叫全局并行队列、全局并发队列,所以全局队列是并行队列
1 // 1. 获取全局队列(与自定义并行队列的区别就是名字显示,其他都一样) 2 dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 3 // 使用全局队列,不需要考虑共享的问题 4 5 // 3. 同步任务 6 for (int i = 0; i < 10; i++) { 7 dispatch_sync(q, ^{ 8 NSLog(@"%@ - %d", [NSThread currentThread], i); 9 }); 10 } 11 12 // 2. 异步任务 13 for (int i = 0; i < 10; i++) { 14 dispatch_async(q, ^{ 15 NSLog(@"%@ - %d", [NSThread currentThread], i); 16 }); 17 }
因为是 DISPATCH_QUEUE_PRIORITY_DEFAULT(默认的优先级)输出结果跟在自定义并行队列里的结果一样
全局队列使用更普遍一些,因为使用起来简单
不需要考虑共享问题:指MRC中,自定义并行队列 dispatch_queue_t q = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);需要自己释放队列dispatch_release(q);
因此,无论是ARC还是MRC,使用默认优先级的全局队列不用考虑共享问题。
唯一跟自定义并行队列的区别是,队列名字不同
4、主队列
是专门在主线程上工作的队列,即专门调度任务在主队列上执行
(1)主队列,异步任务
1 // 1. 获取主队列 2 dispatch_queue_t q = dispatch_get_main_queue(); 3 4 // 2. 异步任务,在主线程上依次顺序执行 5 for (int i = 0; i < 10; i++) { 6 dispatch_async(q, ^{ 7 NSLog(@"%@ - %d", [NSThread currentThread], i); 8 }); 9 }
输出结果:
输出结果说明,主队列上异步任务依次顺序执行,跟串行队列的异步任务不同的是,没有开启子线程。
所以,异步任务是以异步的方式执行,不一定在哪个线程上。有可能在主线程上,也有可能在子线程上。
(2)主队列,同步任务(千万不能使用)
1 // 1. 获取主队列 2 dispatch_queue_t q = dispatch_get_main_queue(); 3 4 // 3. 不要同步任务(死锁!!!) 5 dispatch_sync(q, ^{ 6 NSLog(@"不会输出任何东西"); 7 });
不会输出任何东西,没有任何反应,这种情况叫任务死锁
因为,应用程序已启动,就会建立一个线程,这个线程被称为主线程。而主队列的同步任务会一直等待主线程执行完了,只有程序挂了,主线程才结束,根本就不会执行主队列的同步任务。