Dispatch Queue
Dispatch Queue是用来执行任务的队列,是GCD中最基本的元素之一。
Dispatch Queue分为两种:
Serial Dispatch Queue,按添加进队列的顺序(先进先出)一个接一个的执行
Concurrent Dispatch Queue,并发执行队列里的任务
简而言之,Serial Dispatch Queue只使用了一个线程,Concurrent Dispatch Queue使用了多个线程(具体使用了多少个,由系统决定)。
可以通过两种方式来获得Dispatch Queue
//创建串行队列
dispatch_queue_t serialQueue=dispatch_queue_create("com.xxx", DISPATCH_QUEUE_SERIAL);
//创建并行行队列
dispatch_queue_t concurrentQueue=dispatch_queue_create("com.xxx", DISPATCH_QUEUE_CONCURRENT);
第一个参数是队列的名称,一般是使用倒序的全域名。虽然可以不给队列指定一个名称,但是有名称的队列可以让我们在遇到问题时更好调试;当第二个参数为nil时返回Serial Dispatch Queue,如上面那个例子,当指定为DISPATCH_QUEUE_CONCURRENT时返回Concurrent Dispatch Queue。
第二种方式是直接获取系统提供的Dispatch Queue。
要获取的Dispatch Queue无非就是两种类型:
Main Dispatch Queue
Global Dispatch Queue / Concurrent Dispatch Queue
一般只在需要更新UI时我们才获取Main Dispatch Queue,其他情况下用Global Dispatch Queue就满足需求了:
//主线程
dispatch_queue_t mainQueue = dispatch_get_main_queue();
//子线程
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_after
dispatch_after能让我们添加进队列的任务延时执行,比如想让一个Block在10秒后执行:
double delayInSeconds = 10.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
NSLog(@"电充好了");
});
NSEC_PER_SEC表示的是秒数,它还提供了NSEC_PER_MSEC表示毫秒。
这句dispatch_after的真正含义是在10秒后把任务添加进队列中,并不是表示在10秒后执行
dispatch_group
//执行组 集团
可能经常会有这样一种情况:我们现在有3个Block要执行,我们不在乎它们执行的顺序,我们只希望在这3个Block执行完之后再执行某个操作。这个时候就需要使用dispatch_group了:
dispatch_group_t g=dispatch_group_create();
dispatch_group_async(g, concurrentQueue, ^(void){
NSLog(@"电充好了");
});
dispatch_group_notify(g, concurrentQueue, ^(void){
NSLog(@"电充好了1");
});
输出的顺序与添加进队列的顺序无关,因为队列是Concurrent Dispatch Queue,但“completed”的输出一定是在最后的:电充好了1
除了使用dispatch_group_notify函数可以得到最后执行完的通知外,还可以使用
需要注意的是,dispatch_group_wait实际上会使当前的线程处于等待的状态,也就是说如果是在主线程执行dispatch_group_wait,在上面的Block执行完之前,主线程会处于卡死的状态。可以注意到dispatch_group_wait的第二个参数是指定超时的时间,如果指定为DISPATCH_TIME_FOREVER(如上面这个例子)则表示会永久等待,直到上面的Block全部执行完,除此之外,还可以指定为具体的等待时间,根据dispatch_group_wait的返回值来判断是上面block执行完了还是等待超时了。
dispatch_group_t g=dispatch_group_create();
dispatch_group_async(g, concurrentQueue, ^(void){
NSLog(@"电充好了");
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER)
NSLog(@"电充好了1");
dispatch_barrier_async
dispatch_barrier_async就如同它的名字一样,在队列执行的任务中增加“栅栏”,在增加“栅栏”之前已经开始执行的block将会继续执行,当dispatch_barrier_async开始执行的时候其他的block处于等待状态,dispatch_barrier_async的任务执行完后,其后的block才会执行
- (void)writeFilek{
[[NSUserDefaults standardUserDefaults]setInteger:7 forKey:@"integer"];
}
- (void)readFile{
NSLog(@"%i",[[NSUserDefaults standardUserDefaults]integerForKey:@"integer"]);
}
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(globalQueue, ^(void){
[self readFile];
});
dispatch_async(globalQueue, ^(void){
[self readFile];
});
dispatch_async(globalQueue, ^(void){
[self readFile];
});
dispatch_barrier_async(globalQueue, ^(void){
[self writeFilek];
[self readFile];
});
dispatch_async(globalQueue, ^(void){
[self readFile];
});
dispatch_async(globalQueue, ^(void){
[self readFile];
});
dispatch_async(globalQueue, ^(void){
[self readFile];
});
由于这个队列是一个Concurrent Dispatch Queue,能同时并发多少线程是由系统决定的,如果添加dispatch_barrier_async的时候,其他的block(包括上面4个block)还没有开始执行,那么会先执行dispatch_barrier_async里的任务,其他block全部处于等待状态。如果添加dispatch_barrier_async的时候,已经有block在执行了,那么dispatch_barrier_async会等这些block执行完后再执行。
dispatch_apply
dispatch_apply会将一个指定的block执行指定的次数。如果要对某个数组中的所有元素执行同样的block的时候,这个函数就显得很有用了,用法很简单,指定执行的次数以及Dispatch Queue,在block回调中会带一个索引,然后就可以根据这个索引来判断当前是对哪个元素进行操作:
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(10, globalQueue, ^(size_t tem){
NSLog(@"%lu",tem);
});
NSLog(@"completed");
由于是Concurrent Dispatch Queue,不能保证哪个索引的元素是先执行的,但是“completed”一定是在最后打印,因为dispatch_apply函数是同步的,执行过程中会使线程在此处等待,所以一般的,我们应该在一个异步线程里使用dispatch_apply函数:
dispatch_suspend / dispatch_resume
某些情况下,我们可能会想让Dispatch Queue暂时停止一下,然后在某个时刻恢复处理,这时就可以使用dispatch_suspend以及dispatch_resume函数:
//暂停
dispatch_suspend(globalQueue)
//恢复
dispatch_resume(globalQueue)
暂停时,如果已经有block正在执行,那么不会对该block的执行产生影响。dispatch_suspend只会对还未开始执行的block产生影响。