说来惭愧,学习iOS那么久,关于GCD都是直接看网上的资料,还没有系统的看过官方文档,所以打算好好读一读官方文档,加深自己对GCD的了解。
什么是GCD
什么是GCD呢?
从《Objective-C高级编程 iOS与OS X多线程和内存管理》一书中这样介绍的:
Grand Central Dispatch(GCD)是异步执行任务的技术之一。开发者只需要定义想执行的任务并追加到适当的Dispatch Queue 中, GCD就能生成必要的线程并计划执行任务。由于线程管理是作为系统的一部分来实现的,因此可统一管理,也可执行任务,这样久比以前的线程更有效率。也就是说,GCD用我们难以置信的非常简洁技术方法,实现了极为复杂繁琐的多线程编程。
但是我在看官方文档中并没有看到这段话,估计是更新了吧。最新的官网上是这么说的:
Grand Central Dispatch(GCD)包含了用来增强系统对多核硬件的并发执行能力的语言特性、runtime库和系统加强包。GCD在系统级别上运行,可以更好的适应所有运行的应用程序的需求,并以一种平衡的方式将它们和可用的资源相匹配。
总体来说,GCD使用方便且高效,但是使用的时候需要注意,避免线程阻塞、死锁等状况发生。
GCD的API
1.Dispatch Queue
Dispatch Queue 是一个轻量级的对象,你只需提交你想执行任务的代码块。Dispatch Queue 按照FIFO(先进先出)顺序调用提交给它的代码块。一个串型队列一次只能调用一个块,但是不同的队列可以同时调用它们自己的代码块。
Dispatch Queue 的类型分为两种:DISPATCH_QUEUE_SERIAL 和 DISPATCH_QUEUE_CONCURRENT。
Dispatch Queue Type | 说明 |
---|---|
DISPATCH_QUEUE_SERIAL | 串型队列,每次只能执行一个任务 |
DISPATCH_QUEUE_CONCURRENT | 并行队列,可以同时执行多个任务 |
2.获得Dispatch Queue
我们了解了Dispatch Queue的类型,那么如何获得DispatchQueue呢?我们有两张方法获取Dispatch Queue:1.通过dispatch_queue_create 自己创建 2.获取系统提供的Dispatch Queue(Main Dispatch Queue & Global Dispatch Queue)。
2.1 dispatch_queue_create
我们可以直接通过dispatch_queue_create函数生成Dispatch Queue
//第一个参数:queue的唯一标识 ;第二个参数:队列的类型 ,为NULL是默认为DISPATCH_QUEUE_SERIAL
dispatch_queue_create(<#const char * _Nullable label#>, <#dispatch_queue_attr_t _Nullable attr#>)
dispatch_queue_t queue = dispatch_queue_create("com.serial0.myqueue", NULL);
dispatch_queue_t serialQueue = dispatch_queue_create("com.serial1.myqueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.concurrentQueue.myqueue", DISPATCH_QUEUE_CONCURRENT);
其中dispatch_queue_create函数的第一个参数是创建的队列的唯一标识,官方文档推荐我们使用反向dns的命名格式(com.examle.myqueue)。你可以将标识设置为NULL,但是不推荐,因为给队列添加标识,能够在调试的时候清楚的知道当前的执行队列,还能在crashLog中给出提示。 你也可以通过dispatch_queue_get_label函数获取当前队列的标识。第二个参事设置队列的类型,置NULL为DISPATCH_QUEUE_SERIAL类型。
2.2 Main Dispatch Queue & Global Dispatch Queue
Main Dispatch Queue 为主线程,有且仅有一个,所以主线程是serial Dispatch Queue。追加到主线程到任务在主线程的RunLoop中执行,所以用户界面的UI更新这些必须放到主线程中,而一些耗时的操作尽量避免放到主线程。
Global Dispatch Queue 为全局线程,是Concurrent Dispatch Queue,有四个优先级,分别为:
- 高优先级(DISPATCH_QUEUE_PRIORITY_HIGH)
- 默认优先级(DISPATCH_QUEUE_PRIORITY_DEFAULT)
- 低优先级(DISPATCH_QUEUE_PRIORITY_LOW)
- 后台优先级(DISPATCH_QUEUE_PRIORITY_BACKGROUND)
系统提供的Dispatch Queue的总结如下:
名称 | 类型 | 说明 |
---|---|---|
Main Dispatch Queue | Serial Dispatch Queue | 主线程执行 |
Global Dispatch Queue(High Priority) | Concurrent Dispatch Queue | 高优先级 |
Global Dispatch Queue(Default Priority) | Concurrent Dispatch Queue | 默认优先级 |
Global Dispatch Queue(Low Priority) | Concurrent Dispatch Queue | 低优先级 |
Global Dispatch Queue(Background Priority) | Concurrent Dispatch Queue | 后台优先级 |
系统Dispatch Queue 获取如下:
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_queue_t highQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_queue_t defaultQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t lowQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
GCD使用
我们了解了GCD,也知道获取Dispatch Queue 的方法,那么如何使用呢?系统的API里有许多方法,这里先介绍最常见的两种方法dispatch_async和dispatch_sync。
dispatch_async 异步执行
异步执行,不受其他代码块的影响,一旦放入队列中就执行。
dispatch_queue_t defaultQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(defaultQueue, ^{
NSLog(@"actionA:%@", [NSThread currentThread]);
});
dispatch_async(defaultQueue, ^{
NSLog(@"actionB:%@", [NSThread currentThread]);
});
dispatch_async(defaultQueue, ^{
NSLog(@"actionC:%@", [NSThread currentThread]);
});
dispatch_async(defaultQueue, ^{
NSLog(@"actionD:%@", [NSThread currentThread]);
});
代码执行结果如下:
2018-03-22 15:48:10.332231+0800 LCChat[93644:7440393] actionC:{number = 5, name = (null)}
2018-03-22 15:48:10.332232+0800 LCChat[93644:7440394] actionA:{number = 3, name = (null)}
2018-03-22 15:48:10.332242+0800 LCChat[93644:7440395] actionD:{number = 6, name = (null)}
2018-03-22 15:48:10.332252+0800 LCChat[93644:7440397] actionB:{number = 4, name = (null)}
说明异步执行的代码块顺序是不可控制的。
dispatch_sync 同步执行
dispatch_queue_t defaultQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_sync(defaultQueue, ^{
NSLog(@"actionA:%@", [NSThread currentThread]);
});
dispatch_sync(defaultQueue, ^{
NSLog(@"actionB:%@", [NSThread currentThread]);
});
dispatch_sync(defaultQueue, ^{
NSLog(@"actionC:%@", [NSThread currentThread]);
});
dispatch_sync(defaultQueue, ^{
NSLog(@"actionD:%@", [NSThread currentThread]);
});
代码执行的结果如下:
2018-03-22 15:51:18.971098+0800 LCChat[93697:7443960] actionA:{number = 1, name = main}
2018-03-22 15:51:18.971344+0800 LCChat[93697:7443960] actionB:{number = 1, name = main}
2018-03-22 15:51:18.971475+0800 LCChat[93697:7443960] actionC:{number = 1, name = main}
2018-03-22 15:51:18.971603+0800 LCChat[93697:7443960] actionD:{number = 1, name = main}
证明同步执行遵守FIFO,必须等前一个block完成才能进行下一个block。
总结
基本的GCD的介绍和常用的GCD使用在上文中已经大致的介绍了,其实你看官方文档,你会发现GCD还有很多强大的功能,比如dispatch_once,dispatch I/Od等,这些不常用的,等有空的时候再一一细读。