什么是GCD?
GCD是异步执行任务的技术之一。它通过向系统管理的调度队列(dispatch queues)中提交工作,在多核硬件上同时执行代码。
GCD特点
- 提高执行效率:
由于GCD提供的是系统级的线程管理,因此其执行效率要高于其它多线程处理方法。
- 使用起来非常简单:
//在后台线程中执行
dispatch_async(queue, ^{
//想执行的任务
});
//在主线程中执行
dispatch_async(dispatch_get_main_queue(), ^{
//想执行的任务
});
仅一行代码就能在指定线程中执行相应操作。
Dispatch Queue
Dispatch Queue:调度队列,用来管理你提交给它的任务。所有的Dispatch Queue都是先进先出(FIFO)的数据结构。
Dispatch Queue种类 | 说明 |
---|---|
serial Dispatch Queue | 串行队列,按照它们添加到队列中的顺序逐一执行任务,经常用来同步访问指定资源 |
Concurrent Dispatch Queue | 并发队列,同时执行一个或多个任务,仍以添加到队列中的顺序来开始一个任务的 |
Main Dispatch Queue | 主队列,是一个全局可用的串行队列,在应用程序的主线程中执行任务 |
serial Dispatch Queue:
Concurrent Dispatch Queue:
创建和管理调度队列
创建串行队列和并行队列都是使用dispatch_queue_create()函数,它有两个参数,第一个指明队列的名称,第二个参数指明队列是串行的还是并发的。其返回值是dispatch_queue_t类型。
创建串行的调度队列
dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("com.example.MyQueue", NULL);
diapatch_async(mySerialDispatchQueue, ^{
})
- 当生成多个Serial Dispatch Queue时,各个Serial Dispatch Queue将并行执行。
- 在多个线程更新相同资源导致数据竞争时使用serial Dispatch Queue。
注意:
虽然使用dispatch_queue_create()可以生成任意多个Serial Dispatch Queue,但如果过多的使用多线程,就会消耗大量的内存,引起大量的上下文切换,大幅度降低系统的响应性能。
创建并发的调度队列
在执行不发生数据竞争等问题的处理时,使用Concurrent Dispatch Queue。
dispatch_queue_t myConcurrentDispatchQueue = dispatch_queue_create("com.example.myQueue",
DISPATCH_QUEUE_CONCURRENT);
diapatch_async(myConcurrentDispatchQueue, ^{
})
对于Concurrent Dispatch Queue来说,不管生成多少个,由于XNU内核只使用有效管理的线程,因此不会出现Serial Dispatch Queue的问题。
全局并发调度队列
系统为每个应用程序提供了一个全局的并发队列,你不需要显示的来创建它们。你只需要使用diapatch_get_global_queue()函数来使用它们:
dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
它有4个执行优先级,分别是:
- DISPATCH_QUEUE_PRIORITY_DEFAULT:默认优先级
- DISPATCH_QUEUE_PRIORITY_HIGH:高优先级
- DISPATCH_QUEUE_PRIORITY_LOW:低优先级
- DISPATCH_QUEUE_PRIORITY_BACKGROUND:后台优先级
注意:
dispatch_get_global_queue()的第二个函数是预留给未来扩展使用的,现在,你总是传0即可。
主调度队列
主调度队列的获取方法:
dispatch_queue_t mainQueue = dispatch_get_main_queue();
主调度队列是在主线程中执行的,因为主线程只有一个,所以主调度队列自然就是Serial Dispatch Queue。
调度队列的内存管理
调度队列和其它的调度对象不能使用ARC进行自动内存管理,必须由程序员手动进行内存管理。这是因为调度队列和调度对象不具有作为Objective-C对象来处理的技术。
- 通过dispatch_queue_create()函数生成的dispatch_queue_t在使用结束后,要通过dispatch_release()函数释放。
dispatch_release(mySerialDispatchQueue);
- 相应的也可以通过dispatch_retain函数,是dispatch对象的引用计数加1。
dispatch_retain(myConcurrentDispatchQueue);
- tips:
dispatch_queue_t queue = dispatch_queue_create("com.example.myQueue",
DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"block on queue!");
})
dispatch_release(queue);
使用dispatch_async()函数将Block追加Concurrent Dispatch Queue中,并立即通过dispatch_release()函数进行释放是否可以呢?
该源代码完全没有问题。将Block追加到Concurrent Dispatch Queue中时,Block就会通过dispatch_retain函数持有Dispatch Queue。无论是serial Dispatch Queue还是Concurrent Dispatch Queue都是一样的。一旦Block执行结束,就会通过dispatch_release函数释放该Block持有的Dispatch Queue。
注意:
你不需要对全局的调度队列进行retain或release操作,包括并发的调度队列或主调度队列。任何retain或release队列的操作都会被忽视。