dispatch_groupを使った非同期処理の終了の待機と通知
dispatch_group_tとそれに関連するメソッド概要
概要 | |
---|---|
dispatch_group_t | dispatch_group_tの型 |
dispatch_group_create() | dispatch_group_tを作成する |
dispatch_group_wait( group, time ) | groupの処理が完了するまで待機する |
dispatch_group_enter( group ) | 次から始まるコードがgroupの処理となる宣言 |
dispatch_group_leave( group ) | groupの処理を終了する |
dispatch_group_notify( group, queue, blocks) | groupの処理が完了した時点で指定したqueueでblocksを実行する |
dispatch_groupでできること
それぞれ非同期で行われる処理の待機
-(void)hoge {
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("com.dispatch.queue.concurrent", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, queue, ^{
NSLog(@"start 1");
sleep(1);
NSLog(@"end 1");
});
dispatch_group_async(group, queue, ^{
NSLog(@"start 2");
sleep(3);
NSLog(@"end 2");
});
dispatch_group_async(group, queue, ^{
NSLog(@"start 3");
sleep(2);
NSLog(@"end 3");
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"finish group wait");
}
出力結果
2015-11-19 16:31:40.741[4626:148842] start 1
2015-11-19 16:31:40.741[4626:148849] start 2
2015-11-19 16:31:40.742[4626:148850] start 3
2015-11-19 16:31:41.745[4626:148842] end 1
2015-11-19 16:31:42.743[4626:148850] end 3
2015-11-19 16:31:43.747[4626:148849] end 2
2015-11-19 16:31:43.748[4626:148843] finish group wait
並列キューで実行されるコードを、dispatch_group_async内で実行します。
1,2,3はほぼ同時に実行され、並列キュー内ではそれぞれ1秒,3秒,2秒のスリープを入れています。
結果としては、スリープの時間に応じてendのログが出力され、最終的にdispatch_group_waitでの待機が終了し、最後のログが出ています。
このように、非同期で実行されるメソッドをまとめて待機したい場合などに有用となります。
別スレッドで実行され、blocksで結果が返る処理をdispatch_groupで待機
まず間違っている例
-(void)hoge2 {
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("com.dispatch.queue.concurrent", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, queue, ^{
NSLog(@"start 1");
[self hogehogeWithWaitTime:1 completion:^{
NSLog(@"block 1");
}];
NSLog(@"end 1");
});
dispatch_group_async(group, queue, ^{
NSLog(@"start 2");
[self hogehogeWithWaitTime:3 completion:^{
NSLog(@"block 2");
}];
NSLog(@"end 2");
});
dispatch_group_async(group, queue, ^{
NSLog(@"start 3");
[self hogehogeWithWaitTime:2 completion:^{
NSLog(@"block 3");
}];
NSLog(@"end 3");
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"finish group wait");
}
-(void)hogehogeWithWaitTime:(int)time completion:(void(^)(void))completion{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(time);
completion();
});
}
出力結果
2015-11-19 16:51:17.065[4741:157719] start 1
2015-11-19 16:51:17.065[4741:157718] start 2
2015-11-19 16:51:17.065[4741:157726] start 3
2015-11-19 16:51:17.065[4741:157719] end 1
2015-11-19 16:51:17.065[4741:157718] end 2
2015-11-19 16:51:17.065[4741:157726] end 3
2015-11-19 16:51:17.066[4741:157717] finish group wait
2015-11-19 16:51:18.070[4741:157727] block 1
2015-11-19 16:51:19.067[4741:157728] block 3
2015-11-19 16:51:20.069[4741:157719] block 2
求めているものがコレならばオッケーですが、今回は以下の様な結果が欲しい。
期待される出力結果
2015-11-19 16:51:17.065[4741:157719] start 1
2015-11-19 16:51:17.065[4741:157718] start 2
2015-11-19 16:51:17.065[4741:157726] start 3
2015-11-19 16:51:17.065[4741:157719] end 1
2015-11-19 16:51:17.065[4741:157718] end 2
2015-11-19 16:51:17.065[4741:157726] end 3
2015-11-19 16:51:18.070[4741:157727] block 1
2015-11-19 16:51:19.067[4741:157728] block 3
2015-11-19 16:51:20.069[4741:157719] block 2
2015-11-19 16:51:17.066[4741:157717] finish group wait
こうなってほしい。
修正版
-(void)hoge2 {
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("com.dispatch.queue.concurrent", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"start 1");
[self hogehogeWithWaitTime:1 completion:^{
NSLog(@"block 1");
dispatch_group_leave(group);
}];
NSLog(@"end 1");
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"start 2");
[self hogehogeWithWaitTime:3 completion:^{
NSLog(@"block 2");
dispatch_group_leave(group);
}];
NSLog(@"end 2");
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"start 3");
[self hogehogeWithWaitTime:2 completion:^{
NSLog(@"block 3");
dispatch_group_leave(group);
}];
NSLog(@"end 3");
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"finish group wait");
}
結果
2015-11-19 17:06:26.271[4812:163357] start 1
2015-11-19 17:06:26.271[4812:163358] start 2
2015-11-19 17:06:26.271[4812:163357] end 1
2015-11-19 17:06:26.271[4812:163364] start 3
2015-11-19 17:06:26.271[4812:163358] end 2
2015-11-19 17:06:26.271[4812:163364] end 3
2015-11-19 17:06:27.273[4812:163357] block 1
2015-11-19 17:06:28.274[4812:163358] block 3
2015-11-19 17:06:29.274[4812:163365] block 2
2015-11-19 17:06:29.274[4812:163356] finish group wait
startとendがちょっと順不同になったが、並列処理なのでおおよそオッケー。
dispatch_group_enterとleaveは1対1となり、すべてのenterに対してleaveが実行されるまでgroupの処理としてみなされます。
そのため、dispatch_group_waitは全てのleaveが実行後に解除されるので、blocksの実行まで待てるというわけ。
dispatch_semaphore_tでも同様のことができますが、dispatch_group_enter&leaveでは内部でsemaphoreを使っているそうなので、どちらを使っても良いでしょう。
また、スレッドを止めるのではなく非同期でgroup内の処理を通知したい場合は、dispatch_group_notifyを使います。
dispatch_group_notifyで処理を止めずに終わったら通知
-(void)notifyGroupWithAsyncBlocks:(void(^)(void))blocks {
NSLog(@"%s",__PRETTY_FUNCTION__);
dispatch_queue_t queue = dispatch_queue_create("com.gclue.dispatch_queue.concurrent", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"start 1");
[self executeAsyncBlock:^{
NSLog(@"finish wait 1");
dispatch_group_leave(group);
} withWaitTime:1];
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"start 2");
[self executeAsyncBlock:^{
NSLog(@"finish wait 2");
dispatch_group_leave(group);
} withWaitTime:3];
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"start 3");
[self executeAsyncBlock:^{
NSLog(@"finish wait 3");
dispatch_group_leave(group);
} withWaitTime:2];
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
blocks();
});
NSLog(@"*** finish: %s",__PRETTY_FUNCTION__);
}
-(void)executeAsyncBlock:(void(^)(void))completion withWaitTime:(NSTimeInterval)time{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(time);
completion();
});
}
実行
[self notifyGroupWithAsyncBlocks:^{
NSLog(@"called notify block");
}];
結果
2016-01-04 19:02:37.435 DispatchGroupSample[15357:325094] -[ViewController notifyGroupWithAsyncBlocks:]
2016-01-04 19:02:37.435 DispatchGroupSample[15357:325094] *** finish: -[ViewController notifyGroupWithAsyncBlocks:]
2016-01-04 19:02:37.436 DispatchGroupSample[15357:325136] start 2
2016-01-04 19:02:37.436 DispatchGroupSample[15357:325134] start 1
2016-01-04 19:02:37.436 DispatchGroupSample[15357:325135] start 3
2016-01-04 19:02:38.442 DispatchGroupSample[15357:325134] finish wait 1
2016-01-04 19:02:39.442 DispatchGroupSample[15357:325135] finish wait 3
2016-01-04 19:02:40.442 DispatchGroupSample[15357:325136] finish wait 2
2016-01-04 19:02:40.442 DispatchGroupSample[15357:325094] called notify block