iOS--GCD的API的理解与使用
什么是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");
});
打印结果为:
blk1
blk0
blk2
done
由于多个线程并行执行,所以执行的顺序会发生改变,但是done一定是在最后输出。
Dispatch Group.png另外,在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_barrier_async.png
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
待续。。。。