Dispatch Group用于创建一组队列。有时你可能需要等dispatch队列中的所有任务完成了才执行一个任务。当所有任务都在一个串行队列里面的时候,你只需要将最后一个任务加到队列最后就可以了。但如果你在使用并行队列的时候或者面对多个队列的时候,看起来就没那么简单了。这种情况下,你就可以使用Dispatch Group。下面的代码演示了Dispatch Group的基本用法,代码中,3个block被添加到了全局dispatch队列上,当所有block都完成后,最终一个block会在主线程队列上执行。
dispatch_queue_t serialQueue = dispatch_queue_create("SerialQueue", NULL);
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,dispatch_get_main_queue(), ^{NSLog(@"done");});
dispatch_group_notify(group,serialQueue, ^{sleep(3); NSLog(@"done");});
dispatch_group_notify(group,serialQueue, ^{sleep(3); NSLog(@"done");});
不管是在何种队列上,dispatch group都会监控任务的完成情况。当它发现所有任务都完成后,最终一个任务就会被加到队列上。这就是使用dispatch group的方法。
首先,用dispatch_group_create函数创建一个dispatch_group_t类型的dispatch group。因为函数名字中包含的”create”,当你不再需要dispatch group后,要将它释放掉。像对dispatch队列那样,用dispatch_release函数将它释放掉。
接着,像用dispatch_async函数那样,用dispatch_group_async函数将block加到指定的队列上。与dispatch_async函数的不同之处在于,这个函数需要一个dispatch group来作为第一个参数。当调用dispatch_group_async函数的时候,block就和group关联起来了。和将block加到dispatch队列上的情况类似,当一个block和dispatch group关联起来后,block也就会通过dispatch_retain函数获得dispatch group的所有权。当block执行完毕后,也会通过dispatch_release函数释放掉对group的所有权。你不需要去关心block和group是如何关联起来的。
最后,dispatch_group_notify函数将一个block加到了一个dispatch队列上。这个block会在group中的所有任务完成后被执行。函数的第一个参数是一个要通知的dispatch group。当和这个group关联的所有任务完成后,第三个参数中的block会被添加到指定的队列(第二个参数)上。不管传递给dispatch_group_notify函数的是何种队列,当block被添加到队列上时,group相关联的所有任务一定已经全部完成了。
另外,如下面代码所示的这样,你还可以使用dispatch_group_wait函数,等待dispatch group的所有任务完成。
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, ^{sleep(3); NSLog(@"blk0");});
dispatch_group_async(group, queue, ^{NSLog(@"blk1");});
dispatch_group_async(group, queue, ^{NSLog(@"blk2");});
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC);
// long result = dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
long result = dispatch_group_wait(group, time);
if (result == 0)
{
/*
* dispatch group相关联的所有任务都已经完成
*/
NSLog(@"11");
}
else
{
/*
* dispatch group相关联的一些任务仍然在执行
*/
NSLog(@"222");
}
等等,那这里的”wait”是什么意思?是指当dispatch_group_wait函数被调用后,函数不会立即返回,当前执行dispatch_group_wait函数的这个线程会停止,当超过指定的等待时间后或者dispatch group相关联的所有任务都完成后,这个线程才会继续,函数才会返回结果。
当等待时间被指定为DISPATCH_TIME_NOW的时候,这个函数可以被用来检查dispatch group相关联的所有任务是否都已经完成。
long result = dispatch_group_wait(group, DISPATCH_TIME_NOW);
2.dispatch_barrier_async
dispatch_barrier_async函数用于等待队列中其他任务完成。前面我们提到过,当你访问数据库或者一个文件的时候,你可以用串行队列来避免数据冲突。但实际上,当有其他更新数据或读取数据的操作在进行的时候,不应该在同时在进行更新数据的操作。但是多个读取操作是可以同时进行的,这样可以更高效的访问数据,这种情况下,更新数据的操作必须放到一个串行队列中,并且只有当串行队列中没有任何更新操作在执行的时候,这些读取操作才能被放到一个并行队列中。你要确保不能在更新操作完成前开始读取操作。你可以用dispatch group和dispatch_set_target_queue函数来实现这种功能,但是会很复杂。GCD提供了一个更好的解决方案,这就是dispatch_barrier_async函数了。下面的代码创建了一个并发队列,然后向队列中添加了一些读取操作。
dispatch_queue_t queue = dispatch_queue_create("SerialQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"read1");
});
dispatch_async(queue, ^{
NSLog(@"read2");
});
dispatch_async(queue, ^{
NSLog(@"read3");
});
dispatch_barrier_async(queue, ^{
NSLog(@"write");
});
dispatch_async(queue, ^{
NSLog(@"read4");
});
dispatch_async(queue, ^{
NSLog(@"read5");
});
dispatch_async(queue, ^{
NSLog(@"read6");
});
如你所见,就是这么简单,只需要用dispatch_barrier_async代替dispatch_async就可以了。
所以,为了更高效的访问数据库或文件,尽情的使用并发队列和dispatch_barrier_async函数吧。
3.dispatch_sync
dispatch_sync函数和dispatch_async函数类似都是用于将一个任务添加到一个队列上,但是dispatch_sync会等待添加到队列上的任务执行完毕。dispatch_async函数名字中的”async”代表异步(asynchronous)。因此,它将一个block添加到一个队列上后,block会异步的执行,dispatch_async不会等待任务执行完毕。如图7-10所示。
与此对应的同步版本(synchronous)——dispatch_sync函数,它将block添加到队列上后,会等到block执行完毕后才返回。如图7-11所示。
像前面dispatch_group_wait函数那一小节中解释过的那样,等待表示当前线程会暂停。例如,你可能想在主线程上使用一个在其他线程上的全局队列上运行的任务的结果,这种情况,你就可以用dispatch_sync函数。
dispatch_queue_t queue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_sync(queue, ^{/* a task */});
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{NSLog(@"Hello?");});
dispatch_queue_t queue = dispatch_get_main_queue(); dispatch_async(queue, ^{
dispatch_sync(queue, ^{NSLog(@"Hello?");});
});
dispatch_queue_t queue =
dispatch_queue_create("com.example.gcd.MySerialDispatchQueue", NULL);
dispatch_async(queue, ^{
dispatch_sync(queue, ^{NSLog(@"Hello?");});
});
4.dispatch_apply
dispatch_apply函数和dispatch_sync函数以及dispatch group有点关系。它用来将一个block多次添加到dispatch队列上,然后等待所有任务完成。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(10, queue, ^(size_t index) {
NSLog(@"%zu", index);
NSLog(@"线程===%d",[NSThread isMainThread]);
});
NSLog(@"done");
可以看到其实线程并不是只是子线程,也可以主线程,原因是当前可用并发队列,系统自动分配的。。。不是一个线程哦,是个队列
因为是执行在全局队列上面的,所以每个任务的执行时间不一定相同。但是,“done”总会在最后执行,因为dispatch_apply函数会等待所有任务完成。
函数中,第一个参数是添加的次数,第二个参数是目标队列,第三个参数就是要添加到目标队列上的block了。在上面这个例子中,可以看到block带有一个参数,这个参数是用来区分是第几次block的,因为block会被多次添加到队列上。比如,如果你想要对一个NSArray中的所有对象进行某些操作的时候,就不需要用for循环的方式了。如下代码所示。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_apply([array count], queue, ^(size_t index) { NSLog(@"%zu: %@", index, [array objectAtIndex:index]); });
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); /* * 在全局dispatch上并发执行 */ dispatch_async(queue, ^{ /* * 在全局dispatch队列上,dispatch_apply会等待所有任务完成。 */ dispatch_apply([array count], queue, ^(size_t index) { /* * 并发地对数组中的所有对象进行一些处理 */ NSLog(@"%zu: %@", index, [array objectAtIndex:index]); }); /* * dispatch_apply添加的所有任务都完成了。 */ /* * 在主线程dispatch队列上异步执行某些任务。 */ dispatch_async(dispatch_get_main_queue(), ^{ /* * 这里是要在主线程队列上执行的任务。 * 比如更新用户界面等等。 */ NSLog(@"done"); }); });