文章目录

使用 dispatch_group 可以用来将多个block组成一组以监测这些Block全部完成或者等待全部完成时发出的消息。

设想下面的循环:

1
2
3
for(id obj in array) {
[self doSomethingIntensiveWith:obj];
}

假定 -doSomethingIntensiveWith: 是线程安全的且可以同时执行多个。一个array通常包含多个元素,这样的话,我们可以很简单地使用GCD来平行运算:

1
2
3
4
5
6
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for(id obj in array) {
dispatch_async(queue, ^{
[self doSomethingIntensiveWith:obj];
});
}

如此简单,我们已经在多核心上运行这段代码了。

当然这段代码并不完美。有时候我们有一段代码要像这样操作一个数组,但是在操作完成后,我们还需要对操作结果进行其他操作:

1
2
3
4
for(id obj in array) {
[self doSomethingIntensiveWith:obj];
}
[self doSomethingWith:array];

这时候使用GCD的 dispatch_async 就悲剧了.我们还不能简单地使用 dispatch_sync 来解决这个问题, 因为这将导致每个迭代器阻塞,就完全破坏了平行计算。

这时候我们就要用到 dispatch group 了,先使用函数 dispatch_group_create 来创建,然后使用函数 dispatch_group_async 来将block提交至一个dispatch queue,同时将它们添加至一个组。所以我们现在可以重新代码:

1
2
3
4
5
6
7
8
9
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
for(id obj in array) {
dispatch_group_async(group, queue, ^{
[self doSomethingIntensiveWith:obj];
});
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
[self doSomethingWith:array];

如果这些工作可以异步执行,那么我们可以更风骚一点,将函数 -doSomethingWith: 放在后台执行。我们使用 dispatch_group_async 函数建立一个block在组完成后执行:

1
2
3
4
5
6
7
8
9
10
11
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
for(id obj in array) {
dispatch_group_async(group, queue, ^{
[self doSomethingIntensiveWith:obj];
});
}
//等group里的task都执行完后执行notify方法里的内容,相当于把wait方法及之后要执行的代码合到一起了
dispatch_group_notify(group, queue, ^{
[self doSomethingWith:array];
});

不仅所有数组元素都会被平行操作,后续的操作也会异步执行,并且这些异步运算都会将程序的其他部分考虑在内。

当我们遇到这种情况: -doSomethingIntensiveWith: 需要异步执行时,我们就无法使用 dispatch_group_async了:

1
2
3
4
5
6
7
8
9
10
dispatch_group_t group = dispatch_group_create();
for(id obj in array) {
dispatch_group_enter(group);
[self doSomethingIntensiveWith:obj complete:^(id result){
dispatch_group_leave(group);
}];
}
dispatch_group_notify(group, queue, ^{
[self doSomethingWith:array];
});

但是这两个函数一定要配合使用,有 enter 就要有 leave,这样才能保证功能完整实现。

注意:如果 -doSomethingWith: 需要 在主线程中执行,比如操作GUI,那么我们只要将main queue而非全局队列传给dispatch_group_notify函数就行了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"group1");
});
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"group2");
});
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:3];
NSLog(@"group3");
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"updateUi");
});