一、什么是GCD?
GCD全称Grand Central Dispatch
。它是苹果为多核的并行运算提出的解决方案,所以会自动合理地利用更多的CPU内核(比如双核、四核),最重要的是它会自动管理线程的生命周期(创建线程、调度任务、销毁线程),完全不需要我们管理,我们只需要告诉干什么就行。Grand Central Dispatch
和NSOperationQueue
是iOS平台上最常用的并发编程API,而NSOperationQueue
是基于GCD进行封装,本文通过GCD介绍iOS并发编程的基础概念和常见的死锁产生原因。
二、相关概念
GCD中有2个核心概念:任务、队列。
- 任务:执行什么操作。
- 队列:用来存放任务。
下面简单介绍下相关概念。
任务
分为同步任务和异步任务。
同步任务:就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回,程序也不会接着往下执行。按照这个定义,其实绝大多数函数都是同步调用。对应API:
dispatch_sync(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)
异步任务
异步任务:当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调(Handler机制)来通知调用者。对应API:
dispatch_async(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)
队列
分为串行队列和并行队列。
串行队列:串行队列的特点是队列内的线程是一个一个执行,直到结束。
代表队列为系统默认提供的主队列,对应获取API:dispatch_get_main_queue()
。
并行队列:并行队列的特点是可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)。
注意:并发功能只有在异步(dispatch_async)函数下才有效,因为异步函数才具备开启新线程的能力(并不一定会开启新线程),而同步函数只能在当前线程中执行不具备开启线程的能力。
代表队列为系统默认提供的全局并行队列,对应获取API:dispatch_get_global_queue(<#long identifier#>, <#unsigned long flags#>)
。
除了以上两个系统默认提供的队列外,还可以手动创建相关队列,自定义串行或并行。
//串行队列
dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", NULL);
dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_SERIAL);
//并行队列
dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_CONCURRENT);
根据任务和队列组合总共有以下4种情况:
同步执行 | 异步执行 | |
---|---|---|
串行队列 | 当前线程,一个一个执行 | 其他线程,一个一个执行 |
并行队列 | 当前线程,一个一个执行 | 开很多线程,一起执行 |
三、什么是死锁
死锁(deadlock) 通常是当多个线程在相互等待着对方的结束时,就会发生死锁,这时程序可能会被卡住。此时就导致了deadlock!
死锁产生的常见原因是在当前串行队列里面同步执行任务,比如在主线程执行如下代码就会造成死锁:
NSLog(@"1");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"2");
});
这是由于在主线程同步执行((dispatch_sync(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)
)任务,这会导致主队列被阻塞,然后在主队列已经阻塞的情况下,将任务加入主队列(dispatch_get_main_queue()
),导致程序无法继续运行。解决方法是异步执行任务或者将任务加入其他队列。
参考资料:
关于iOS多线程,你看我就够了
GCD死锁大作战
转载请注明出处:
GCD的使用及死锁产生原因分析