前天学了 IOS 的 NSOperation 基本使用,我们得知 NSOperation 也是基于 IOS GCD(Grand Central Dispatch)实现,其实在做 IOS 开发中 GCD 已经基本上能够满足大部分需求。作为 IOS 开发工程师很有必要对 GCD 做个全面了解,今天一边写 demo 一边对比总结一下 GCD 使用。
GCD 是 Grand Central Dispatch 的简称,它是基于 C 语言的。如果使用 GCD,完全由系统管理线程,我们不需要编写线程代码。只需定义想要执行的任务, 然后添加到适当的调度队列 (dispatch queue)。GCD 会负责创建线程和调度你的任务,系统直接提供线程管理。
使用线程就离不开线程队列的执行方式和任务的执行方式,大致分以下几个:
上面了解到 Dispatch 通过分发开发者提供的不同 queue 来调度任务,我们来看下 GCD 有哪些队列。
队列类型 | 创建方式 |
主线程串行队列(mian) |
dispatch_get_main_queue(); |
自定义串行队列(Serial) |
dispatch_queue_create("MySerialQueue", DISPATCH_QUEUE_SERIAL); |
自定义并行队列(Concurrent) |
dispatch_queue_create("MyConcurrentQueue", DISPATCH_QUEUE_CONCURRENT); |
全局并行队列(global) |
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); |
添加执行任务的方式大致有两种:dispatch_sync 同步执行任务函数,不会开启新的线程,dispatch_async 异步执行任务函数,会开启新的线程,接下来分别看下他们的使用示例;
- dispatch_queue_t mainQueue = dispatch_get_main_queue();
- dispatch_async(mainQueue, ^{
- //在block里写要执行的任务(代码)
- NSLog(@"currentThread1:%@ isMainThread:%@",[NSThread currentThread],[[NSThread currentThread] isMainThread]?@"YES":@"NO");
- });
- dispatch_async(mainQueue, ^{
- //在block里写要执行的任务(代码)
- NSLog(@"currentThread2:%@ isMainThread:%@",[NSThread currentThread],[[NSThread currentThread] isMainThread]?@"YES":@"NO");
- });
- dispatch_async(mainQueue, ^{
- //在block里写要执行的任务(代码)
- NSLog(@"currentThread3:%@ isMainThread:%@",[NSThread currentThread],[[NSThread currentThread] isMainThread]?@"YES":@"NO");
- });
注意:
主线程串行队列执行在主线程中,一般使用都是调用 dispatch_async 添加任务,使用 dispatch_sync 添加任务会导致死锁问题。
运行结果:
通过运行结果可以得知,虽然任务是通过 dispatch_async 添加执行的,但是并没有创建子线程去执行任务,而是执行在主线程中。
有时我们需要创建一个串行任务并且需要执行在子线程中,这时就需要创建串行队列,进行异步添加调用方式执行任务。
- dispatch_queue_t mySerialQueue = dispatch_queue_create("MySerialQueue", DISPATCH_QUEUE_SERIAL);
- dispatch_async(mySerialQueue, ^{
- //在block里写要执行的任务(代码)
- NSLog(@"currentThread1:%@ isMainThread:%@",[NSThread currentThread],[[NSThread currentThread] isMainThread]?@"YES":@"NO");
- });
- dispatch_async(mySerialQueue, ^{
- //在block里写要执行的任务(代码)
- NSLog(@"currentThread2:%@ isMainThread:%@",[NSThread currentThread],[[NSThread currentThread] isMainThread]?@"YES":@"NO");
- });
- dispatch_async(mySerialQueue, ^{
- //在block里写要执行的任务(代码)
- NSLog(@"currentThread3:%@ isMainThread:%@",[NSThread currentThread],[[NSThread currentThread] isMainThread]?@"YES":@"NO");
- });
运行结果:
可以看出串行队列异步执行创建了一个线程,并且是依次执行。
相对与串行任务队列,有时我们需要同时执行多个任务,这个时候我们就需要使用并行任务队列了,这里我们采用全部并行队列。
- dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
- dispatch_async(globalQueue, ^{
- //在block里写要执行的任务(代码)
- NSLog(@"currentThread1:%@ isMainThread:%@",[NSThread currentThread],[[NSThread currentThread] isMainThread]?@"YES":@"NO");
- });
- dispatch_async(globalQueue, ^{
- //在block里写要执行的任务(代码)
- NSLog(@"currentThread2:%@ isMainThread:%@",[NSThread currentThread],[[NSThread currentThread] isMainThread]?@"YES":@"NO");
- });
- dispatch_async(globalQueue, ^{
- //在block里写要执行的任务(代码)
- NSLog(@"currentThread3:%@ isMainThread:%@",[NSThread currentThread],[[NSThread currentThread] isMainThread]?@"YES":@"NO");
- });
这里可以根据不同的优先级创建不同的全部任务队列
- DISPATCH_QUEUE_PRIORITY_HIGH //高优先级
- DISPATCH_QUEUE_PRIORITY_DEFAULT //默认优先级
- DISPATCH_QUEUE_PRIORITY_LOW //低优先级
- DISPATCH_QUEUE_PRIORITY_BACKGROUND //后台执行
运行结果:
三个任务几乎是同时进行的,而且动态为每个任务开辟了一个线程用于执行任务。并行队列尽量使用异步添加任务的方式调用,同步添加任务方式调用不会创建子线程而是任务全部同时执行在主线程中导致 UI 卡死。
- dispatch_queue_t myConcurrentQueue = dispatch_queue_create("MyConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
- dispatch_async(myConcurrentQueue, ^{
- //在block里写要执行的任务(代码)
- NSLog(@"currentThread1:%@ isMainThread:%@",[NSThread currentThread],[[NSThread currentThread] isMainThread]?@"YES":@"NO");
- });
- dispatch_async(myConcurrentQueue, ^{
- //在block里写要执行的任务(代码)
- NSLog(@"currentThread2:%@ isMainThread:%@",[NSThread currentThread],[[NSThread currentThread] isMainThread]?@"YES":@"NO");
- });
- dispatch_async(myConcurrentQueue, ^{
- //在block里写要执行的任务(代码)
- NSLog(@"currentThread3:%@ isMainThread:%@",[NSThread currentThread],[[NSThread currentThread] isMainThread]?@"YES":@"NO");
- });
运行结果和全部并行队列类似。
有时无论我们串行执行多任务还是并行执行多任务,需要这个任务组全部执行完毕之后通知我们,这里就需要用到队列组了。
- dispatch_group_t group = dispatch_group_create();
- dispatch_queue_t myConcurrentQueue = dispatch_queue_create("MyConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
- //dispatch_group_async用于把不同的任务归为一组
- //dispatch_group_notify当指定组的任务执行完毕之后,执行给定的任务
- dispatch_group_async(group, myConcurrentQueue, ^{
- NSLog(@"currentThread-1:%@ isMainThread:%@",[NSThread currentThread],[[NSThread currentThread] isMainThread]?@"YES":@"NO");
- });
- dispatch_group_async(group, myConcurrentQueue, ^{
- NSLog(@"currentThread-2:%@ isMainThread:%@",[NSThread currentThread],[[NSThread currentThread] isMainThread]?@"YES":@"NO");
- });
- dispatch_group_async(group, myConcurrentQueue, ^{
- NSLog(@"currentThread-3:%@ isMainThread:%@",[NSThread currentThread],[[NSThread currentThread] isMainThread]?@"YES":@"NO");
- });
- dispatch_group_notify(group, myConcurrentQueue, ^{
- NSLog(@"currentThread-g:%@ isMainThread:%@",[NSThread currentThread],[[NSThread currentThread] isMainThread]?@"YES":@"NO");
- });
运行结果:
既然是多任务执行,就难以避免会出现多任务同时访问同一数据的问题,就会遇到同步的问题,串行队列不太会遇见数据同步的问题,但是并行队列一定会有数据同步的问题,IOS GCD 考虑的很全面通过调用 dispatch_barrier_async 函数添加任务来隔离其他的任务,起到一个栅栏的作用,它等待所有位于 barrier 函数之前的操作执行完毕后执行, 并且在 barrier 函数执行之后, barrier 函数之后的操作才会得到执行, 该函数需要同 dispatch_queue_create 函数生成的 concurrent Dispatch Queue 队列一起使用。
- dispatch_queue_t conCurrentQueue = dispatch_queue_create("myConCurrentQueue", DISPATCH_QUEUE_CONCURRENT);
- dispatch_async(conCurrentQueue, ^{
- NSLog(@"currentThread-1:%@ isMainThread:%@",[NSThread currentThread],[[NSThread currentThread] isMainThread]?@"YES":@"NO");
- });
- dispatch_async(conCurrentQueue, ^{
- NSLog(@"currentThread-2:%@ isMainThread:%@",[NSThread currentThread],[[NSThread currentThread] isMainThread]?@"YES":@"NO");
- });
- dispatch_barrier_async(conCurrentQueue, ^{
- [NSThread sleepForTimeInterval:1.0];
- NSLog(@"currentThread-b:%@ isMainThread:%@",[NSThread currentThread],[[NSThread currentThread] isMainThread]?@"YES":@"NO");
- [NSThread sleepForTimeInterval:1.0];
- });
- dispatch_async(conCurrentQueue, ^{
- NSLog(@"currentThread-3:%@ isMainThread:%@",[NSThread currentThread],[[NSThread currentThread] isMainThread]?@"YES":@"NO");
- });
- dispatch_async(conCurrentQueue, ^{
- NSLog(@"currentThread-4:%@ isMainThread:%@",[NSThread currentThread],[[NSThread currentThread] isMainThread]?@"YES":@"NO");
- });
运行结果:
dispatch_once 保证在 app 运行期间,block 中的代码只执行一次
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- NSLog(@"只执行一次");
- //这个block里的代码,在程序执行过程中只会执行一次。
- //比如在这里些单例的初始化
- // static YourClass *instance = nil;
- // instance = [[YourClass alloc] init];
- });
dispatch_after 延时添加到队列
- double delayInSeconds = 3.0;
- dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
- dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
- //dispatch_after函数是延迟执行某个任务,任务既可以在mainQueue中进行也可以在其他queue中进行.既可以在serial队列里执行也可以在concurrent队列里执行。
- NSLog(@"currentThread:%@ isMainThread:%@",[NSThread currentThread],[[NSThread currentThread] isMainThread]?@"YES":@"NO");
- });
dispatch_apply 把一项任务提交到队列中多次执行,具体是并行执行还是串行执行由队列本身决定
- NSArray *array = [NSArray arrayWithObjects:@"who",@"is",@"lcj", nil];
- dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
- dispatch_apply([array count], queue, ^(size_t index) {
- NSLog(@"%@ : currentThread:%@ isMainThread:%@",[array objectAtIndex:index],[NSThread currentThread],[[NSThread currentThread] isMainThread]?@"YES":@"NO");
- });
用了 GCD 之后更见佩服 Apple 公司了,封装的 sdk 真是太完善了。简单的总结了如何使用 GCD,希望在以后的使用中有个大致了解。
来源: http://www.cnblogs.com/whoislcj/p/6404279.html