前言
在上一篇文章iOS多线程开发—GCD(二)中我介绍了什么是多线程,GCD与线程的关系,以及如何创建队列和简单的使用,在这篇文章中我讲重点总结一下GCD一些高级用法。
创建dispatch_queue_t需要注意的问题
我们可以通过dispatch_get_global_queue(long identifier, unsigned long flags);获取全局队列,通过dispatch_get_main_queue();获取主队列,通过dispatch_queue_create(const char *_Nullable label,dispatch_queue_attr_t _Nullable attr);创建并行或者串行队列。
我们需要注意的是,dispatch_get_global_queue(long identifier, unsigned long flags);dispatch_get_main_queue();这两种方法都是让我们获取系统已经创建好的队列,因此我们不需要对这些队列进行内存管理,系统会帮助我们在合适的时间释放这些队列。
- 在《Objective-C 高级编程》这本书中,作者写到当我们使用dispatch_queue_create(const char _Nullable label,dispatch_queue_attr_t _Nullable attr);去创建队列的时候,我们需要手动对这个队列进行内存管理,虽然我们使用了ARC,但是dispatch_queue_t并不是一个对象类型,因此ARC无法对它进行内存管理这时候我们就需要使用dispatch_release(queue)去释放这个队列。但是根据我自己的实践和查阅苹果官方文档显示,如果我们使用了ARC,那么我们是不需要使用dispatch_release和dispatch_retain去管理队列的,事实上在ARC中使用dispatch_release和dispatch_retain会报错。*
GCD API使用
1. dispatch_queue_create
dispatch_queue_create(const char *_Nullable label,dispatch_queue_attr_t _Nullable attr);
这个方法可以让我们创建一个队列,第一个参数是队列的名字,也可以为空,但是不利于debug。第二个参数是队列的类型,如果写NULL,那么默认是串行队列,DISPATCH_QUEUE_CONCURRENT将创建并行队列。
//串行队列
dispatch_queue_t serialQueue = dispatch_queue_create("com.jiaxiang.serialQueue", DISPATCH_QUEUE_SERIAL);
//并行队列
dispatch_queue_t concurrentQueue1 = dispatch_queue_create("com.jiaxiang.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
理论上我们可以在这里创建任意多个队列,并且多个串行队列是可以并行执行的。但是当我们使用了过多的串行队列时,我们会创建相应的串行线程,过多的线程创建将消耗系统资源。
2. Main Dispatch Queue / Global Dispatch Queue
//获取主队列,在主线程上执行
dispatch_queue_t mainQueue = dispatch_get_main_queue();
//获取默认优先级的全局队列,并行执行
dispatch_queue_t concurrentQueue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_get_global_queue(long identifier, unsigned long flags);第一个参数指定队列的优先级,第二个参数是flag,大多时间使用0。
优先级分为四级,从高到低依次为,高,默认,低,后台
#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_queue_t serialQueue = dispatch_queue_create("com.jiaxiang.serialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(concurrentQueue1, ^{
//同步任务追加到并行队列中
NSLog(@"Concurrent Queue");
NSLog(@"Concurrent Queue%@",[NSThread currentThread]);
dispatch_async(mainQueue, ^{
//异步任务追加到主队列中
NSLog(@"Main Queue");
NSLog(@"Main Queue%@",[NSThread currentThread]);
});
});
NSLog(@"Done");
NSLog(@"Done%@",[NSThread currentThread]);
上面这个使用,让我们可以把耗时操作放到并行队列中,并且将结果放到主队列中执行。
3.dispatch_set_target_queue
dispatch_set_target_queue(dispatch_object_t object,dispatch_queue_t _Nullable queue);这个方法让我们将一个队列的执行优先级设置为另一个队列的优先级。第一个参数是变更优先级的队列,第二个参数是目标优先级队列。第一个参数不能是系统提供的Main Queue 和 Global Dispatch Queue。
//将serialQueue的优先级设置为与concurrentQueue2相同
dispatch_queue_t serialQueue = dispatch_queue_create("com.jiaxiang.serialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t concurrentQueue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_set_target_queue(serialQueue, concurrentQueue2);
dispatch_queue_t serialQueue1 = dispatch_queue_create("com.jiaxiang.serialQueue1", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t serialQueue2 = dispatch_queue_create("com.jiaxiang.serialQueue2", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t serialQueue3 = dispatch_queue_create("com.jiaxiang.serialQueue2", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t serialQueue4 = dispatch_queue_create("com.jiaxiang.serialQueue3", DISPATCH_QUEUE_SERIAL);
dispatch_async(serialQueue, ^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"0");
});
dispatch_async(serialQueue1, ^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"1");
});
dispatch_async(serialQueue2, ^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"2");
});
dispatch_async(serialQueue3, ^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"3");
});
dispatch_async(serialQueue4, ^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"4");
});
以上创建的五个串行队列在执行的时候是并行执行的,它们每次执行的先后顺序并不固定。但是当我们使用dispatch_set_target之后它们将变成串行执行。
dispatch_queue_t serialQueue1 = dispatch_queue_create("com.jiaxiang.serialQueue1", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t serialQueue2 = dispatch_queue_create("com.jiaxiang.serialQueue2", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t serialQueue3 = dispatch_queue_create("com.jiaxiang.serialQueue2", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t serialQueue4 = dispatch_queue_create("com.jiaxiang.serialQueue3", DISPATCH_QUEUE_SERIAL);
dispatch_set_target_queue(serialQueue1, serialQueue);
dispatch_set_target_queue(serialQueue2, serialQueue);
dispatch_set_target_queue(serialQueue3, serialQueue);
dispatch_set_target_queue(serialQueue4, serialQueue);
dispatch_async(serialQueue, ^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"0");
});
dispatch_async(serialQueue1, ^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"1");
});
dispatch_async(serialQueue2, ^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"2");
});
dispatch_async(serialQueue3, ^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"3");
});
dispatch_async(serialQueue4, ^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"4");
});
4,dispatch_after
dispatch_after(dispatch_time_t when,dispatch_queue_t queue, dispatch_block_t block);第一个参数指定多长时间以后将任务追加到指定的队列,第二个参数指定执行任务的队列,第三个参数是追加的任务。需要清楚的是任务并不是在指定的时间之后执行,而是任务在指定的时间后被追加到队列,因此任务执行的时间很可能在指定的时间之后。并且这个方法执行的是异步任务,因此当指定的队列是main queue的时候也不用担心死锁。
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"Hello");
});
NSLog(@"Done After");
5,Dispatch Group
在使用GCD的时候,我们有时候会希望当我们执行完并行队列之后,能够追加一个任务在所有并行队列中的任务执行完之后再执行。这时候我们就可以使用dispatch group来实现这个需求。
dispatch_queue_t concurrentQueue1 = dispatch_queue_create("com.jiaxiang.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, concurrentQueue1, ^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"1");
});
dispatch_group_async(group, concurrentQueue1, ^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"2");
});
dispatch_group_async(group, concurrentQueue1, ^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"3");
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"Done");
});
6.dispatch_barrier_async
当我们需要让并行队列中的1,2,3这几个任务执行完之后,执行任务4,然后再执行任务5,6,7以此避免在在写入数据的同同时读取数据,造成数据竞争,这时候我们可以使用dispatch_barrier_async。
dispatch_queue_t concurrentQueue1 = dispatch_queue_create("com.jiaxiang.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQueue1, ^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"1");
});
dispatch_async(concurrentQueue1, ^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"2");
});
dispatch_async(concurrentQueue1, ^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"3");
});
dispatch_barrier_async(concurrentQueue1, ^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"4");
});
dispatch_async(concurrentQueue1, ^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"5");
});
dispatch_async(concurrentQueue1, ^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"6");
});
dispatch_async(concurrentQueue1, ^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"7");
});
7.dispatch_apply
该函数按指定的次数将指定的任务追加到队列中,并等待全部处理执行结束。
dispatch_queue_t concurrentQueue1 = dispatch_queue_create("com.jiaxiang.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_apply(10, concurrentQueue1, ^(size_t index) {
NSLog(@"%zu",index);
});
NSLog(@"Done");
8.dispatch_suspend / dispatch_resume
dispatch_suspend(queue),挂起指定的队列,队列中已经执行的任务无影响,为执行的任务停止执行。
dispatch_resume(queue),恢复指定的队列,队列中为执行的任务继续执行。
9.Dispatch Semaphore
通过信号量来控制队列中任务的执行情况。