为什么要用GCD
更好的利用多核、更好的利用并发
什么时候用到GCD
解决耗时、阻塞主队列(主线程)、完成并发任务时用到GCD。
1、GCD简介
GCD
是Grand Central Dispatch
简称,是一套低层API,提供了一种新的方法来进行并发程序编写。从基本功能上讲,GCD有点像NSOperationQueue
,他们都允许程序将任务切分为多个单一任务然后提交至工作队列来并发地或者串行地执行。GCD比之 NSOpertionQueue更底层更高效
,并且它不是Cocoa框架的一部分。
GCD是为多核的并行运算提出的解决方案
, 除了代码的平行执行能力,GCD还提供高度集成的事件控制系统。可以设置句柄来响应文件描述符、mach ports(Mach port 用于 OS X上的进程间通讯)、进程、计时器、信号、用户生成事件。这些句柄通过GCD来并发执行。
GCD
的API很大程度上基于block
,当然,GCD也可以脱离block来使用,比如使用传统c机制提供函数指针和上下文指针。实践证明,当配合block使用时,GCD非常简单易用且能发挥其最大能力。
2、GCD的工作原理
让程序平行排队的特定任务,根据可用的处理资源,安排他们在任何可用的处理器核心上执行任务。一个任务可以是一个函数(function)或者是一个block。GCD的底层依然是用线程
实现,不过这样可以让程序员不用关注实现的细节。
核心概念
(1)任务:执行什么操作。在 GCD中就是一个 Block
,所以添加任务十分方便。任务有两种执行方式: 同步执行和异步执行,同步(sync
) 和 异步(async
) 的主要区别在于会不会阻塞当前线程,直到 Block 中的任务执行完毕。
- 同步执行:它会阻塞当前线程并等待
Block
中的任务执行完毕,然后当前线程才会继续运行。
- 异步执行:当前线程会直接往下执行,它不会阻塞当前线程。
(2)队列:用来存放任务。系统提供了两个队列,一个是主队列MainDispatchQueue
,一个全局队列GlobalDispatchQueue
, 即串行队列 和 并行队列。
- 串行队列:放到串行队列的任务,GCD 会 FIFO(先进先出) 地取出来一个,执行一个,然后取下一个,这样一个一个的执行。会将任务插入主线程的RunLoop当中去执行,我们可以使用它来更新UI。
- 并行队列:放到并行队列的任务,GCD 也会 FIFO的取出来,但不同的是,它取出来一个就会放到别的线程,然后再取出来一个又放到另一个的线程。这样由于取的动作很快,忽略不计,看起来,所有的任务都是一起执行的。不过需要注意,GCD 会根据系统资源控制并行的数量,所以如果任务很多,它并不会让所有任务同时执行。有高、默认、低和后台4个优先级。
3、GCD的优势
- 易用: GCD比之NSThread更简单易用。由于GCD基于work unit而非像thread那样基于运算,所以GCD可以控制诸如等待任务结束、监视文件描述符、周期执行代码以及工作挂起等任务。基于block的血统导致它能极为简单得在不同代码作用域之间传递上下文。
- 效率: GCD被实现得如此轻量和优雅,使得它在很多地方比之专门创建消耗资源的线程更实用且快速。这关系到易用性:导致GCD易用的原因有一部分在于你可以不用担心太多的效率问题而仅仅使用它就行了。
- 性能: GCD自动根据系统负载来增减线程数量,这就减少了上下文切换以及增加了计算效率。
串行、并行、同步、异步
串行:队列中的任务一个一个顺序执行。(一个任务执行->等待返回结果->下一个任务执行)
并行:队列中的任务一个一个顺序执行。(一个任务一个任务的顺序执行,不用等待返回结果)
同步:就是在发出一个功能调用时,在没有得到结果之前,该调用就不继续往下运行(调用)。也就是必须一件一件事做,等前一件做完了才能做下一件事。(一个线程,当前线程。)
异步:异步的概念和同步相对。当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的任务在完成后,通过状态、通知和回调来通知调用者。(多个线程,开辟出来的新线程。)
串行队列同步:队列中的任务一个一个顺序执行。不会开辟新线程。(一个任务执行->等待返回结果->下一个任务执行)
//获取当前线程
NSLog(@"主线程 = %@",[NSThread currentThread]);
{
//串行队列同步
//dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)
//label:queue的标记。dispatch_queue_attr_t:创建的队列是串行还是并行。DISPATCH_QUEUE_CONCURRENT 并行,DISPATCH_QUEUE_SERIAL / NULL 串行
dispatch_queue_t queue = dispatch_queue_create("zhouhao", NULL);
dispatch_sync(queue, ^{
for (NSInteger i = 0; i<10; i++) {
NSLog(@"当前线程 = %@ -- i = %ld",[NSThread currentThread],(long)i);
}
});
dispatch_sync(queue, ^{
for (NSInteger i = 0; i<10; i++) {
NSLog(@" 当前线程 = %@ -- j = %ld",[NSThread currentThread],(long)i);
}
});
}
//Output
** Demo[5072:269390] ****主线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main}**
** Demo[5072:269390] ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- i = 0**
** Demo[5072:269390] ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- i = 1**
** Demo[5072:269390] ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- i = 2**
** Demo[5072:269390] ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- i = 3**
** Demo[5072:269390] ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- i = 4**
** Demo[5072:269390] ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- i = 5**
** Demo[5072:269390] ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- i = 6**
** Demo[5072:269390] ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- i = 7**
** Demo[5072:269390] ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- i = 8**
** Demo[5072:269390] ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- i = 9**
** Demo[5072:269390] ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- j = 0**
** Demo[5072:269390] ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- j = 1**
** Demo[5072:269390] ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- j = 2**
** Demo[5072:269390] ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- j = 3**
** Demo[5072:269390] ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- j = 4**
** Demo[5072:269390] ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- j = 5**
** Demo[5072:269390] ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- j = 6**
** Demo[5072:269390] ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- j = 7**
** Demo[5072:269390] ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- j = 8**
** Demo[5072:269390] ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- j = 9**
串行队列异步:任务一个一个的顺序执行。会开辟新线程。(一个任务执行->等待返回结果->下一个任务执行)
//获取当前线程
NSLog(@"主线程 = %@",[NSThread currentThread]);
{
//串行队列异步
//dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)
//label:queue的标记。dispatch_queue_attr_t:创建的队列是串行还是并行。DISPATCH_QUEUE_CONCURRENT 并行,DISPATCH_QUEUE_SERIAL / NULL 串行
dispatch_queue_t queue = dispatch_queue_create("zhouhao", NULL);
dispatch_async(queue, ^{
for (NSInteger i = 0; i<10; i++) {
NSLog(@"当前线程 = %@ -- i = %ld",[NSThread currentThread],(long)i);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i<10; i++) {
NSLog(@" 当前线程 = %@ -- j = %ld",[NSThread currentThread],(long)i);
}
});
}
Output
** Demo[6253:331370] ****主线程**** = <NSThread: 0x7fbfd3504f20>{number = 1, name = main}**
** Demo[6253:331494] ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- i = 0**
** Demo[6253:331494] ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- i = 1**
** Demo[6253:331494] ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- i = 2**
** Demo[6253:331494] ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- i = 3**
** Demo[6253:331494] ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- i = 4**
** Demo[6253:331494] ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- i = 5**
** Demo[6253:331494] ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- i = 6**
** Demo[6253:331494] ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- i = 7**
** Demo[6253:331494] ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- i = 8**
** Demo[6253:331494] ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- i = 9**
** Demo[6253:331494] ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- j = 0**
** Demo[6253:331494] ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- j = 1**
** Demo[6253:331494] ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- j = 2**
** Demo[6253:331494] ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- j = 3**
** Demo[6253:331494] ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- j = 4**
** Demo[6253:331494] ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- j = 5**
** Demo[6253:331494] ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- j = 6**
** Demo[6253:331494] ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- j = 7**
** Demo[6253:331494] ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- j = 8**
** Demo[6253:331494] ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- j = 9**
并行队列同步执行:任务一个一个执行,不会开辟新线程(这个一直有疑问,不知道怎么理解,有知道的请告知。)
//获取当前线程
NSLog(@"主线程 = %@",[NSThread currentThread]);
{
//并行队列同步
dispatch_queue_t queue = dispatch_queue_create("zhouhao", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
NSLog(@"当前线程 = %@ -- 执行循序1",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"当前线程 = %@ -- 执行循序2",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"当前线程 = %@ -- 执行循序3",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"当前线程 = %@ -- 执行循序4",[NSThread currentThread]);
});
}
Output
** Demo[6272:332352] ****主线程**** = <NSThread: 0x7fee33404790>{number = 1, name = main}**
** Demo[6272:332352] ****当前线程**** = <NSThread: 0x7fee33404790>{number = 1, name = main} -- ****执行循序****1**
** Demo[6272:332352] ****当前线程**** = <NSThread: 0x7fee33404790>{number = 1, name = main} -- ****执行循序****2**
** Demo[6272:332352] ****当前线程**** = <NSThread: 0x7fee33404790>{number = 1, name = main} -- ****执行循序****3**
** Demo[6272:332352] ****当前线程**** = <NSThread: 0x7fee33404790>{number = 1, name = main} -- ****执行循序****4**
并行队列异步执行:开辟新线程,有几个任务就开辟几个线程,任务执行是无序的。
//获取当前线程
NSLog(@"主线程 = %@",[NSThread currentThread]);
{
//并行队列异步
dispatch_queue_t queue = dispatch_queue_create("zhouhao", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"当前线程 = %@ -- 执行循序1",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"当前线程 = %@ -- 执行循序2",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"当前线程 = %@ -- 执行循序3",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"当前线程 = %@ -- 执行循序4",[NSThread currentThread]);
});
}
Output
** Demo[6312:333812] ****主线程**** = <NSThread: 0x7fbf4be04f70>{number = 1, name = main}**
** Demo[6312:333917] ****当前线程**** = <NSThread: 0x7fbf4bc0c3e0>{number = 2, name = (null)} -- ****执行循序****1**
** Demo[6312:333905] ****当前线程**** = <NSThread: 0x7fbf4bf8e910>{number = 3, name = (null)} -- ****执行循序****2**
** Demo[6312:333938] ****当前线程**** = <NSThread: 0x7fbf4bf90d30>{number = 5, name = (null)} -- ****执行循序****4**
** Demo[6312:333924] ****当前线程**** = <NSThread: 0x7fbf4be13f80>{number = 4, name = (null)} -- ****执行循序****3**
iOS支持多个层次的多线程编程,层次越高的抽象程度越高,使用也越方便
1、NSThread:是三种方法里面相对轻量级的,更直观地控制线程对象(优点)。但需要管理线程的生命周期、同步、加锁问题,这会导致一定的性能开销(缺点)
2、NSOperation:是基于OC实现的,NSOperation以面向对象的方式封装了需要执行的操作,不必关心线程管理、同步等问题。NSOperation是一个抽象基类,iOS提供了两种默认实现:NSInvocationOperation和NSBlockOperation,当然也可以自定义NSOperation。NSOperation是基于GCD实现,但是相对于GCD来说可控性更强,并且可以加入操作依赖。
3、GCD:GCD是异步执行任务的技术之一。它更倾向于任务操作的管理,更面向过程。
如果在主队列中使用同步sync,程序会卡住(造成了死锁)。如果在非主队列的串行队列中使用同步,则不会造成卡住现象。以及下图情况也会造成卡住
关于GCD的问题。
1、并行队列,顺序执行1000个任务,任务执行的时间各不相同。怎样才能使其也顺序的结束?
2、一个子线程A,执行完毕后,得到结果a,A不结束,然后返回主线程用a更新B,然后再回到A使用a执行C。怎么做?
3、
内容持续更新中。。。