iOS多线程编程-GCD
一、什么是GCD
Grand Central Dispatch (GCD)是支持 iOS 和 OS X 多核硬件系统,并发执行任务的技术之一,是苹果公司为多核的并行运算提出的解决方案,是 NSThread 高度抽象的产物。GCD 一般将应用程序中记述的线程管理用的代码在系统级中实现,完全由系统管理线程,我们不需要编写线程代码,只需要定义想执行的任务并追加到适当的 Dispatch Queue 中,GCD 就能生成必要的线程并计划执行任务。因此线程管理是作为系统的一部分来实现的,不需要我们去管理。
二、多线程编程
1. 调度队列(dispatch queue)
1.1 苹果官方对 GCD 的说明。
开发者要做的只是定义想执行的任务并追加到适当的 Dispatch Queue 中。
这句话用源代码表示如下:
dispatch_async (queue, ^{
/*
* 需要执行的任务
*/
})
1.2 系统提供了许多预定义的 dispatch queue,包括可以在主线程中执行的 dispatch queue,另一个是所有应用程序都能够使用的 concurrent dispatch queue。也可以通过 GCD的 API 生成 dispatch queue。dispatch queue 是严格的按照追加的顺序(先进先出 FIFO ,First -In -First -Out)执行处理。
1.3 dispatch queue 按先进先出的顺序,串行或并行地执行任务
> serial dispatch queue 等待现在执行处理结束,才开始执行下一个任务
> concurrent dispatch queue 不用等待现在执行中处理结束,可以并发执行多个任务
三、创建 dispatch queue
1. 通过 GCD 的 API 生成 Dispatch queue。
1.1 通过 dispatch_queue_create 函数可生成 dispatch queue。以下源代码生成了 Serial Dispatch Queue。
dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("com.example.gcd.mySerialDispatchQueue",NULL) ;
> 该函数的第一个参数指定 Serial Dispatch Queue 的名称。像此源代码这样,Dispatch Queue 的名称推荐使用应用程序 ID 这种逆序全程域名(FQDN,fully qualified domain name)。该名称在 Xcode 和 Instruments 的调试器中作为 Dispatch Queue 名称表示。另外该名称也出现在应用程序崩溃时所产生的 CrashLog 中。我们命名时应遵循这样的原则:对我们编程人员来说简单易懂,对用户来说也要易懂。如果嫌命名麻烦设为 NULL 也可以,但你在调试中一定后悔没有为 Dispatch Queue 署名。
> 第二个参数为 NULL 。
> 如果过多使用多线程,就会消耗大量内存,引起大量的上下文切换,大幅度降低系统的响应性能。
1.2 通过 dispatch_queue_create 函数可生成 dispatch queue。以下源代码生成了 Concurrent Dispatch Queue。
dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("com.example.gcd.mySerialDispatchQueue", DISPATCH_QUEUE_CONCURRENT) ;
> dispatch_queue_create 函数的返回值为表示 Dispatch Queue 的 "dispatch_queue_t类型"
2. 获取系统标准提供的 Dispatch Queue
2.1 Main Dispatch Queue
> 是在主线程中执行的 Dispatch Queue 。因为主线程只有一个,所以 Main Dispatch Queue 自然就是 Serial Dispatch Queue。
> 追加到 Main Dispatch Queue 的处理在主线程的 RunLoop 中执行。由于在主线程中执行,因此要将用户界面的界面更新等一些必须在主线程中执行的处理追加到 Main Dispatch Queue 使用。
> 获取 Main Dispatch Queue 方法如下:
/*
* Main Dispatch Queue 的获取方法
*/
dispatch_queue _t mainDispatchQueue = dispatch_get_main_queue();
2.2 Global Dispatch Queue
> Global Dispatch Queue 是所有应用程序都能够使用的 Concurrent Dispatch Queue。没有必要通过 dispatch_queue_creat 函数逐个生成 Concurrent Dispatch Queue 。只有获取 Global Dispatch Queue 使用即可。
> Global Dispatch Queue 有4个执行优先级,分别是高优先级(High Priority)、默认优先级(Default Priority)、低优先级(Low Priority)和后台优先级(Background Priority)。通过 XNU 内核管理的用于 Global Dispatch Queue 的线程,将各自使用的 Global Dispatch Queue 的执行优先级作为线程的执行优先级使用。但是通过XNU 内核用于 Global Dispatch Queue 的线程并不能保证实时性,因此执行优先级只是大致的判断。
> 获取 Global Dispatch Queue(默认优先级) 方法如下:
/*
* Global Dispatch Queue 的获取方法
*/
dispatch queue_t globalDispatchQueueDefault = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)
3. 以下列举使用 Main Dispatch Queue 和 Global Dispatch Queue 的源代码:
/*
* 在默认优先级的 Global Dispatch Queue 中执行 Block
*/
dispatch_async (dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{
/*
* 可并行执行的处理
*/
/*
* 在 Main Dispatch Queue 中执行Block
*/
dispatch_async(dispatch_get_main_queue(),^{
/*
* 只能在主线程中的处理
*/
})
})
四、常用的GCD
4.1 dispatch_once
> dispatch_once 函数是保证在应用程序执行中只执行一次指定处理的API。
> 一般常用在单例模式中的创建实例,例如:
static id instance;
static dispatch_once_t once;
dispatch_once(&once,^{instance = self.new};);
4.2 dispatch_after
> 经常会有这样的情况:想在3秒后执行处理。可能不仅限于3秒,总之,这种想在指定时间后执行处理的情况,可使用 dispatch_after 函数来实现。
> 在3秒后将指定的 Block 追加到 Main Dispatch Queue 中的源代码如下:
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC);
dispatch_after(time,dispatch_get_main_queue(),^{
NSLog(@"waiter at least three seconds.")
})
>需要注意的是,dispatch_after 函数并不是在指定的时间后执行处理,而只是在指定时间追加处理到 Dispatch queue。
4.3 Dispatch Group
> 在追加到 Dispatch Queue 中的多个处理全部结束后想执行结束处理,这种情况会经常出现。在使用 Concurrent Dispatch Queue 时或同时使用多个Dispatch Queue 时,源代码就会变得颇为复杂。在这种情况下使用 Dispatch Group。
> 例如追加3个 Block 到 Global Dispatch Queue,这些 Block 如果全部执行完毕,就会执行Main Dispatch Queue 中结束处理用的 Block。
dispatch_group_notify (group,dispatch_get_main_queue(),^{
NSLog (@"结束处理");
})
五、总结
多线程编程实际上是一种易发生各种问题的编程技术。比如多个线程操作相同的资源会导致数据不一致(数据竞争)、停止等待事件中的线程会导致多个线程相互持续等待(死锁)、使用太多的线程会消耗大量你存等。尽管极易发生各种问题,也应当使用多线程编程。因为使用多线程编程可保证应用程序的响应性能,特别是当使用 GCD时,能够极大地方便开发者进行多线程编程。