什么是GCD?
Grand Central Dispatch(GCD)是异步执行任务的技术之一。一般将将应用程序中记述的线程管理用的代码在系统级中实现。开发者只需定义想执行的任务并追加到适当的Dispatch Queue中,GCD就能生成必要的线程并计划执行任务,由于线程管理是作为系统的一部分来实现的,因此可统一管理,也可执行任务,这样就比以前的线程更有效率。
Dispatch Queue
含义是,执行处理的等待队列。 在执行处理时有两种Dispatch Queue
Serial Dispatch Queue // 等待现在执行中处理结束Concurrent Dispatch Queue // 不等待现在执行中处理结束复制代码
举例对比:
dispatch_async(queue, blk1);dispatch_async(queue, blk2);dispatch_async(queue, blk3);dispatch_async(queue, blk4);dispatch_async(queue, blk5);dispatch_async(queue, blk6);dispatch_async(queue, blk7);复制代码
当queue为Serial Dispatch Queue 时,因为需要等待现在执行中的处理结果,所以程序会相继执行1234567。 当queue为Concurrent Dispatch Queue时,因为不需要等待现在执行中的处理结果,所以先执行1,无论1有没有执行完都会执行2,以此类推,顺序是会改变的。此中执行方法开启的线程数量取决于当前系统的状态。
dispatch_queue_create
通过GCD的API生成Dispatch Queue。
dispatch_queue_t queue=dispatch_queue_create("com.like.same", NULL);复制代码
第一个参数指定Serial Dispatch Queue的名称,该名称会出现在应用程序崩溃时所生成的CrashLog中。可以但是不推荐为NULL。 第二个参数指定为NULL。 dispatch_queue_create函数可生成人一多个Dispatch Queue。虽然在一个Serial Dispatch Queue中同时只能执行一个追加处理,但是如果将处理分别追加到四个Serial Dispatch Queue中,各个Serial Dispatch Queue执行一个,即为同时执行四个处理。 通过dispatch_queue_creat函数生成的Dispatch Queue在使用结束后通过dispatch release函数释放。
dispatch_release(queue);复制代码
多个线程更新相同资源导致数据竞争时使用Serial Dispatch Queue。
Main Dispatch Queue/Global Dispatch Queue
获取系统标准提供的Dispatch Queue。
// Main Dispatch Queue的获取方法 dispatch_queue_t mainQueue = dispatch_get_main_queue(); // Global Dispatch Queue高优先级获取方法 dispatch_queue_t highQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); // Global Dispatch Queue默认优先级获取方法 dispatch_queue_t defaultQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // Global Dispatch Queue低优先级获取方法 dispatch_queue_t lowQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); // Global Dispatch Queue后台优先级的获取方法 dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);复制代码
dispatch_set_target_queue
变更生成的Dispatch Queue的执行优先级要使用dispatch_set_target_queue函数。 eg:
dispatch_queue_t myQueue = dispatch_queue_create("com.like.same", NULL); dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0); dispatch_set_target_queue(myQueue, backgroundQueue);复制代码
在必须将不可并行执行的处理追加到多个Serial Dispatch Queue中时,如果使用dispatch_set_target_queue 函数将目标指定为某一个Serial Dispatch Queue,即可防止处理并行执行。
dispatch_after
指定时间后执行处理的情况,可使用dispatch_after函数来实现。 eg:
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC); dispatch_after(time, dispatch_get_main_queue(), ^{ NSLog(@"666"); });复制代码
第一个参数时指定时间用的dispatch_time_t类型的的值。 第二个参数是指定要追加处理的Dispatch Queue。 dispatch_time函数能够获取从第一个参数dispatch_time_t类型值中指定的时间开始,到第二个参数指定的单位时间后的时间。上面的代码中第一个参数代表现在。 dispatch_time函数通常用于计算相对时间,而dispatch_walltime函数用语计算绝对时间。 eg:
dispatch_time_t getDispatchTimeByDate(NSDate * date){ NSTimeInterval interval; double second, subsecond; struct timespec time; dispatch_time_t milestone; interval = [date timeIntervalSince1970]; subsecond = modf(interval, &second); time.tv_sec = second; time.tv_nsec = subsecond * NSEC_PER_SEC; milestone = dispatch_time(&time, 0); return milestone;}复制代码
Dispatch Group
再追加到Dispatch Queue中的多个处理全部请求结束后想要执行结果处理,这种情况下使用Dispatch Group。 eg:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, queue, ^{ NSLog(@"blk0"); }); dispatch_group_async(group, queue, ^{ NSLog(@"blk1"); }); dispatch_group_async(group, queue, ^{ NSLog(@"blk2"); }); dispatch_group_notify(group, queue, ^{ NSLog(@"done"); });复制代码
打印结果为:
blk1blk0blk2done复制代码
由于多个线程并行执行,所以执行的顺序会发生改变,但是done一定是在最后输出。
另外,在Dispatch Group中也可以使用dispatch_group_wait函数仅等待全部处理执行结束。dispatch_group_wait(group, DISPATCH_TIME_FOREVER);复制代码
第二个参数为等待的时间这里使用的是永久,也就是当处理未结束,程序将一直等待下去,中途不能取消。 如果dispatch_group_wait函数的返回值不为0,就意味着虽然经过了指定的时间,但属于Dispatch Group的某一个处理还在执行中,如果返回0,那么全部处理执行结束。 一旦调用dispatch_group_wait函数,该函数处于调用的状态而不反回。
dispatch_barrier_async
dispatch_barrier_async函数会等待追加到Concurrent Dispatch Queue上的并行执行的处理全部结束之后,再将指定的处理追加到该Concurrent Dispatch Queue中。然后由dispatch_barrier_async函数处理完毕之后,Concurrent Dispatch Queue再回复为一般动作(继续并行执行)。 eg:
dispatch_async(queue, blk0); dispatch_async(queue, blk1); dispatch_async(queue, blk2); dispatch_barrier_async(queue, blk); dispatch_async(queue, blk3); dispatch_async(queue, blk4); dispatch_async(queue, blk5);复制代码
执行结果恒为012在前,blk在中,345在后。
使用Concurrent Dispatch Queue和dispatch_barrier_async函数可实现高效率的数据库访问和文件访问。
图例:
dispatch_sync
dispatch_async中的“async”意味着非同步,就是将指定的Block非同步的追加到指定Dispatch Queue中,dispatch_async函数不做任何等待。 “sync”则意味着同步,就是将指定的Block非同步的追加到指定Dispatch Queue中,在追加的block结束前,dispatch_sync函数会一直等待。 eg:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_sync(queue, ^{ NSLog(@"上面处理已经结束"); });复制代码
一旦调用dispatch_sync函数,那么指定的处理执行结束之前,该函数不会返回。可以说是简易版的dispatch_group_wait。 dispatch_barrier_async函数的作用是在等待追加的处理全部结束之后,再追加处理到Dispatch Queue中,此外,它还与dispatch_sync函数相同,会等待追加处理的执行结果。 但是频繁使用dispatch_sync容易造成死锁。 eg:
// 造成死锁 dispatch_queue_t queue = dispatch_get_main_queue(); dispatch_sync(queue, ^{ NSLog(@"????"); }); // 同样造成死锁 dispatch_queue_t queue = dispatch_get_main_queue(); dispatch_async(queue, ^{ dispatch_sync(queue, ^{ NSLog(@"???"); }); });复制代码
dispatch_once
dispatch_once函数是保证在应用程序执行中只执行一次指定处理的API。 单例模式:
static Singleton* _instance = nil;+(instancetype) shareInstance{ static dispatch_once_t onceToken ; dispatch_once(&onceToken, ^{ _instance = [[super allocWithZone:NULL] init] ;}) ;return _instance ;}+(id) allocWithZone:(struct _NSZone *)zone{ return [Singleton shareInstance] ;}-(id) copyWithZone:(struct _NSZone *)zone{ return [Singleton shareInstance] ;}复制代码
dispatch_apply
该函数是dispatch_sync函数和Dispatch Group的关联API。按照指定的次数将指定的Block追加到指定的Dispatch Queue中,并等待全部处理执行结束。 eg:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_apply(10, queue, ^(size_t index) { NSLog(@"%zu",index); });复制代码
由于此函数与dispatch_sync函数一样会等待处理执行结束,所以最好在dispatch_async函数中非同步的执行dispatch_apply函数。
dispatch_suspend/dispatch_resume
dispatch_suspend函数挂起指定的Dispatch Queue。
dispatch_suspend(queue);复制代码
dispatch_resume函数恢复指定的dispatch Queue。
dispatch_resume(queue);复制代码
这两个函数对已经执行的处理没有影响。挂起后,追加到Dispatch Queue中但尚未执行的处理在此之后停止执行。而恢复能使这些处理能够继续执行。
Dispatch Semaphore
待续。。。。