以前讲过多线程之NSOperation,今天来说讲代码更加简洁和高效的GCD。下面说的内容都是基于iOS6之后和ARC下。编程
Grand Central Dispatch(GCD) 是异步执行任务的技术之一。开发者只须要定义想执行的任务并追加到适当的Dispatch Queue中,GCD就能生成必要的线程并计划执行任务。因为线程管理是做为系统的一部分来实现的,所以能够统一管理,也可执行任务,这样就比之前的线程更有效率。GCD用很是简洁的代码,就能够实现多线程编程。多线程
这篇主要讲Dispatch Queue的一些基本东西,后续会加入其余相关内容的介绍。并发
Dispatch Queue是执行处理的等待队列,经过调用dispatch_async
等函数,以block
的形式将任务追加到Dispatch Queue中。Dispatch Queue按照添加进来的顺序(FIFO)执行任务处理。可是在任务执行处理方式上,分为Serial Dispatch Queue
和Concurrent Dispatch Queue
。二者的区别如表格所示异步
Dispatch Queue分类 | 说明 |
---|---|
Serial Dispatch Queue | 串行的队列,每次只能执行一个任务,而且必须等待前一个执行任务完成 |
Concurrent Dispatch Queue | 一次能够并发执行多个任务,没必要等待执行中的任务完成 |
下面用代码来演示下:async
dispatch_async(queue, ^{ NSLog(@"1"); }); dispatch_async(queue, ^{ NSLog(@"2"); }); dispatch_async(queue, ^{ NSLog(@"3"); }); dispatch_async(queue, ^{ NSLog(@"4"); });
若是上面的queue是Serial Dispatch Queue
的话,那么输出的结果必定是1,2,3,4。由于执行顺序是肯定的,而且后续的任务必须在以前的任务执行完成后才能执行。函数
若是是Concurrent Dispatch Queue
的话,那么输出的结果就不必定是1,2,3,4了。由于这些任务都是并发执行,而且不须要等待执行中的任务完成,若是其中任意一个任务完成将当即执行后面的任务。spa
在自定义建立前,咱们先看看系统为咱们提供的几个全局的Dispatch Queue
:线程
名称 | Dispatch Queue 的种类 | 说明 |
---|---|---|
Main Dispatch Queue | Serial Dispatch Queue | 主线程执行 |
Global Dispatch Queue (HIGH) | Concurrent Dispatch Queue | 执行优先级:高 |
Global Dispatch Queue (DEFAULT) | Concurrent Dispatch Queue | 执行优先级:默认 |
Global Dispatch Queue (LOW) | Concurrent Dispatch Queue | 执行优先级:低 |
Global Dispatch Queue (BACKGROUND) | Concurrent Dispatch Queue | 执行优先级:后台 |
从表格中咱们能够知道咱们的主线程就是Serial Dispatch Queue
,而以后的三种Dispatch Queue 则是Concurrent Dispatch Queue
。这也是为何咱们不能把耗时的任务放在主线程里面去操做。3d
若是没有特殊需求,咱们能够直接获取这些queue来执行咱们的任务:调试
//主线程 dispatch_queue_t mainQueue = dispatch_get_main_queue(); //HIGH dispatch_queue_t highQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); //DEFAULT dispatch_queue_t defaultQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //LOW dispatch_queue_t lowQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); //BACKGROUND dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
下面看看如何自定义一个queue:
//串行队列 dispatch_queue_t serialQueue = dispatch_queue_create("com.gcd.serialQueue", DISPATCH_QUEUE_SERIAL); //并发队列 dispatch_queue_t concurrentQueue = dispatch_queue_create("com.gcd.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
经过调用dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)
这个函数。第一个参数是给这个queue起的标识,这个在调试的能够看到是哪一个队列在执行,或者在crash日志中,也能作为提示。第二个是须要建立的队列类型,是串行的仍是并发的。固然你也能够经过dispatch_queue_get_label(dispatch_queue_t queue)
获取你建立queue的名字。
这个也是咱们使用最多的地方,咱们直接调用dispatch_async
这个函数,就能够将咱们要追加的任务添加到队列里面,并当即返回,异步的执行。这个很少讲。
dispatch_async(queue, ^{ NSLog(@"1"); });
这点咱们可能用得不是不少,可是一用很差就出现问题了。当调用这个dispatch_sync
函数的时候,这个线程将不会当即返回,直到这个线程执行完毕。看下下面的代码:
dispatch_sync(queue, ^{ [NSThread sleepForTimeInterval:3]; NSLog(@"2"); });
若是你在主线程里面调用这个函数,那么,很遗憾,主线程将被卡主3秒钟。当主线程调用这个方法的时候,因为是同步,不会当即返回,直到这个里面内容执行完毕才能返回。这个时候无论你这个queue是什么类型,都同样。既然不能当即返回,咱们能够在一个异步执行的线程中,再去调用这个同步方法。
dispatch_async(serialQueue, ^{ NSLog(@"4"); dispatch_sync(queue, ^{ [NSThread sleepForTimeInterval:3]; NSLog(@"5"); }); NSLog(@"6"); });
那这个时候,调用这个方法的时候这个线程将当即返回。而同步在这个线程里面执行。这个时候将输出,4,5,6的结果。
同步执行的死锁问题
如今把上面代码拿出来改下
dispatch_async(serialQueue, ^{ NSLog(@"4"); dispatch_sync(serialQueue, ^{ [NSThread sleepForTimeInterval:3]; NSLog(@"5"); }); NSLog(@"6"); });
这个时候,我把queue的类型设置为串行的类型。这个时候将只会输出4。为何呢?系统调用这个线程的时候,首先输出4,而后继续执行这个里面的同步线程。因为个人这个queue是串行的,也就是后续的任务必须在以前的执行中任务完成后才能继续执行。可是这个同步执行的线程不会当即返回,必须等到它执行完成才能返回。这样最外面的任务无法执行完,而里面的同步线程又不能当即返回,因此就造成了死锁。
所以在你的编程中使用这个API的时候,必定要小心,否则就会造成死锁。