GCD总结

这一篇关于GCD的总结。

首先,什么是GCD呢?

GCD(Grand Center Dispatch)是异步执行任务的技术之一。开发者只需要定义想执行的任务并追加到适当的Dispatch Queue中,GCD就可以生成必要的线程并计划执行任务。它的线程管理是作为系统的一部分来实现的,因此可以统一管理,效率很高并且形式上十分简洁。

就像苹果官方对GCD的说明:开发者只需要定义想执行的任务并追加到适当的Dispatch Queue。

好的,那Dispatch Queue是什么呢?如其名所示,Dispatch Queue是执行处理的等待队列。采用先进先出(FIFO)的形式,按照追加到队列的顺序执行处理。

Dispatch Queue有两种形式,一种是Serial Dispatch Queue,按照任务加入到Queue的顺序,逐个执行,不能改变执行顺序或不想并发执行任务时采用这种形式。但是可以创建多个Serial Dispatch Queue,每个Serial Dispatch Queue虽然只有一个任务在执行,但是多个Serial Dispatch Queue同时执行时依然是多个任务在同时执行;另一种是Concurrent Dispatch Queue,可以并行处理多个任务,并行执行的数量取决于系统的状态,它的执行任务的顺序会根据处理内容和系统状态发送改变。

生成Dispatch Queue有两种形式,第一种是GCD的API,另一种是获取系统提供的Dispatch Queue。

好啦,那先看第一种吧,通过GCD的API生成Dispatch Queue~_^

//生成Serial Dispatch Queue类型的,第一个参数为Queue的名称,第二个指定为NULL                   dispatch_queue_t serialDispatch = dispatch_queue_create("GCD.serialDispatch", NULL);
        
 //生成Concurrent Dispatch Queue类型,第一个参数为Queue的名称,第二个指定为DISPATCH-QUEUE-CONCURRENT
dispatch_queue_t concurrentDispatchQueue = dispatch_queue_create("GCD.concurrentDispatch", DISPATCH_QUEUE_CONCURRENT);
对于第一个参数,可以使用NULL,但是建议署名,对调试有很大帮助。

第二个参数,指定Queue的类型,Serial Dispatch Queue指定为NULLConcurrent Dispatch Queue指定为DISPATCH-QUEUE-CONCURRENT。

dispatch_queue_create()函数返回值为dispatch_queue_t类型


※※※   当SDK版本 < 6.0 时,即使开启了ARC,OS_OBJECT_USE_OBJC宏时没有的,也就是说这时候需要程序员自己释放生成的Dispatch Queue,也就是需要调用dispatch_release()函数释放,调用dispatch_retain()函数在必要的时候持有Dispatch Queue。


这里有一个小问题需要了解的、

dispatch_queue_t concurrentDispatchQueue = dispatch_queue_create("GCD.concurrentDispatch", DISPATCH_QUEUE_CONCURRENT);
        
dispatch_async(concurrentDispatchQueue, ^{
            NSLog(@"concurrent dispatch");
        });
        
dispatch_release(concurrentDispatchQueue);

在这段代码中,向Dispatch Queue添加一个Block后,立即释放,是否可以呢?

答案是可以的,在dispatch_async()函数中添加一个Block到Dispatch Queue中,也就是该Block通过dispatch_async()函数持有该Dispatch Queue,不管是Serial Dispatch Queue 或  Concurrent Dispatch Queue,故即使立即调用dispatch_release()函数释放Dispatch Queue,由于Block还持有该Dispatch Queue,所以Dispatch Queue 不会被废弃,当Block执行完毕后释放Dispatch Queue,此时该Dispatch Queue 不会任何对象持有,故被废弃。


通过GCD的API生成Dispatch Queue已经说明白啦~那就说说通过获取系统标准提供的Dispatch Queue 哈、、

系统提供了Main Dispatch Queue 和 Global Dispatch Queue。

Main Dispatch Queue 是在主线程中执行的Dispatch Queue,主线程只有一个,因此Main Dispatch Queue自然就是Serial Dispatch Queue,追加到Main Dispatch Queue的处理在主线程的RunLoop中执行。

Global Dispatch Queue 是所有应用都能使用的Concurrent Dispatch Queue。它有四个优先级:High Priority(高优先级)、Default Priority(默认优先级)、Low Priority(低优先级)、Background Priority(后台优先级)。通过XNU内核管理的用于Global Dispatch Queue的线程,将各自使用的Global Dispatch Queue的执行优先级作为线程执行的优先级使用。

//获取Main Dispatch Queue
dispatch_queue_t mainDispatchQueue = dispatch_get_main_queue();
        
//获取Global Dispatch Queue
/*
 第一个参数是指定Global Dispatch Queue的优先级
第二个参数是预留值。传递除零以外的任何值可能会导致一个空返回值。
*/
dispatch_queue_t globalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

Dispatch Queue 的生成就说到这里,接下来说的是GCD的其他API。

1.dispatch_set_target_queue : 改变执行优先级

通过dispatch_queue_create()函数生成的,不管是Serial Dispatch Queue,还是Concurrent Dispatch Queue,都使用与默认优先级。通过dispatch_set_target_queue()可以改变Dispatch Queue的执行优先级。如果在多个Serial Dispatch Queue中指定目标为某一个Serial Dispatch Queue,那么原本应该并行执行的多个Serial Dispatch Queue,在目标Serial Dispatch Queue上只能同时执行一个处理。这一点可以用于防止并行执行。

//默认优先级
dispatch_queue_t serialDispatch = dispatch_queue_create("GCD.serialDispatch", NULL);

//高优先级
dispatch_queue_t globalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

//serialDispatch也成为高优先级
dispatch_set_target_queue(serialDispatch, globalDispatchQueue);

2. dispatch_after :将任务在指定时间后加载到Dispatch Queue中,实现在指定时间后再执行处理的情况。 

//DISPATCH_TIME_NOW 表示现在的时间 ,3ull*NSEC_PER_SEC表示3秒                 dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull*NSEC_PER_SEC);
dispatch_after(time, dispatch_get_main_queue(), ^{
            NSLog(@"waited 3sec");
        });

这段代码表示在3秒后将Block加到Main Dispatch Queue。因为Main Dispatch Queue在主线程的RunLoop中执行,比如说在每隔1/60秒执行的RunLoop中,Block最快在3秒后执行,最慢在3+1/60秒后执行。


3.Dispatch Group 

在实际情况中,我们常常希望当追加到Dispatch Queue中的所有处理结束后再执行结果处理。这种在Serial Dispatch Queue中当然是可以的,将想执行的所有任务添加到一个Serial Dispatch Queue中,并在最后追加结束处理就可以啦。但是对于Concurrent Dispatch Queue实现起来相当麻烦啊、鉴于这种情况,Dispatch Group就大有用处啦、那就先看代码吧~

dispatch_queue_t globalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);   //获取一个Concurrent Dispatch Queue
dispatch_group_t group = dispatch_group_create();  //生成一个Dispatch Group
 //追加3个Block到Dispatch Group       
dispatch_group_async(group, globalDispatchQueue, ^{
            NSLog(@"1");
        });
dispatch_group_async(group, globalDispatchQueue, ^{
            NSLog(@"2");
        });
dispatch_group_async(group, globalDispatchQueue, ^{
            NSLog(@"3");
        });
//将结束处理Block追加到主线程        
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
            NSLog(@"finish");
        });

代码在运行时,finish一定是在1,2 ,3执行完毕后,最后执行的,但是1 2 3的执行顺序不确定。

故使用Dispatch Group可以监测这些处理执行的结束 ,一旦监测到所有的处理都结束了,就将结束处理的Block追加到指定的Dispatch Queue中。

除了dispatch_group_notify函数外,还有一个dispatch_group_wait函数。

dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull*NSEC_PER_SEC);
//表示等待3秒后监测Group中的处理是否全部结束,结束返回0,否则没有结束
long result = dispatch_group_wait(group, time);
if (result == 0) {
 //result 为0表示所有处理已经结束
     NSLog(@"finish");
}
else{
     //还有处理尚未结束
     NSLog(@"not finish");
}

好的,dispatch_group_wait函数的功能也写的很清楚啦,它的第一个参数表示监测的Group,第二个参数表示等待的时间,在这个栗子中我写的是等待3秒。假如要永久等待,也就是只要Group中的处理没有完成就一直等待,它的写法是DISPATCH_TIME_FOREVER。

再总结一下dispatch_group_wait函数,它的返回值如果不为0,表示过了等待时间但是还有处理没有完成;返回值为0表示Group中的所有处理均完成。一旦调用了dispatch_group_wait函数,执行该函数的当前线程停止工作,直到函数中的等待时间结束。

一般来说,推荐dispatch_group_notify追加结束处理到主线程,因为代码简洁。


4.dispatch_barrier_async:用于将Dispatch Queue中的并行操作处理完后,再将指定的处理加到该Dispatch Queue,待追加的    处理执行完毕后,Dispatch Queue又开始并行执行。

    常用于高效率的数据库访问和文件访问

dispatch_queue_t globalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
//先并行执行1 2        
dispatch_async(globalDispatchQueue, ^{
            NSLog(@"1");
        });
dispatch_async(globalDispatchQueue, ^{
            NSLog(@"2");
        });
 //执行 barrier        
dispatch_barrier_async(globalDispatchQueue, ^{
            NSLog(@"barrier");
        });
 //并行执行3  4       
dispatch_async(globalDispatchQueue, ^{
            NSLog(@"3");
        });
dispatch_async(globalDispatchQueue, ^{
            NSLog(@"4");
        });

想必执行结果应该很明确啦~

话说有异步执行,就当然有同步执行啦~好的,那我们就来看看同步、^_~

5.dispatch_sync:将指定的Block同步追加到Dispatch Queue中,追加结束之前dispatch_sync函数一直等待。

dispatch_sync(globalDispatchQueue, ^{
            NSLog(@"1");
            [NSThread sleepForTimeInterval:20];
        });
//会等待20秒直到上一个线程结束,才会执行该线程                   dispatch_sync(globalDispatchQueue, ^{
            NSLog(@"2");
        });

如代码所示,如果使用同步追加处理,当前的处理没有结束之前下一个处理会处于等待状态。这样很容易引起死锁。


6.dispatch_apply:按照指定的次数将所有指定的Block追加到指定的Dispatch Queue中,并等待全部处理执行结束。

dispatch_apply(5, globalDispatchQueue, ^(size_t index) {
            NSLog(@"%zu",index);
        });
NSLog(@"apply finish");

7.    dispatch_suspend:挂起指定的Queue

 dispatch_resume:恢复指定的Queue

挂起后追加到Dispatch Queue中但是尚未执行的处理在此之后停止执行。恢复后这些处理继续执行


8.Dispatch Semaphore :持有计数的信号,计数为0时等待,计数大于等于1时减去1不等待。

dispatch_queue_t semaphoreQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
NSMutableArray *mutableArray = [NSMutableArray array];
for (int i = 0; i<1000; i++) {
            dispatch_async(semaphoreQueue, ^{
                [mutableArray addObject:[NSNumber numberWithInt:i]];
            });
        }

来看这段代码,使用Global Dispatch Queue更新NSMutableArray对象,不考虑顺序,将所有的对象添加到NSMutableArray中,执行后由于内存错误导致应用程序异常结束的概率很高。但是通过与dispatch_semaphore_wait配合就能够得到改善、

dispatch_semaphore_wait函数是等待Dispatch Semaphore 的计数值达到大于等于1。当函数返回0时,可安全地执行需要进行排他控制的处理,该处理结束时可通过dispatch_semaphore_signal函数将Dispatch Semaphore的计数值加1。

dispatch_queue_t semaphoreQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
        
        //计数初始值为1,保证只能同时访问一个线程
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
        
        NSMutableArray *mutableArray = [NSMutableArray array];
        for (int i = 0; i<1000; i++) {
            dispatch_async(semaphoreQueue, ^{
                //一直等待,直到Dispatch Semaphore的计数值大于等于1
                dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
                
                //由于初始值为1,将计数值减1后为0,只有一个线程继续向下访问
                [mutableArray addObject:[NSNumber numberWithInt:i]];
                
                //处理结束,计数值加1
                dispatch_semaphore_signal(semaphore);
            });
        }

通过这个栗子,其实这和操作系统的信号量机制的原理是一样的呢、


9.dispatch_once:保证在应用程序中只执行一次指定处理。在生成单例对象时使用,也可用于初始化。

static dispatch_once_t pred;
dispatch_once(&pred,^{
            
                //初始化工作
     });

10. Dispatch I/O :对文件进行简单的结合和切割。


那就是这么多了,有表达不当的,小伙伴们就提出来呦~对您有帮助的请点赞哦~双击666~谢谢大家~下期再会、



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值