前回紹介したdispatch_applyはブロックの処理が終わるまで待ってくれていましたが、dispatch_asyncは別スレッドで処理されてしまうので、どの順番で処理されるのかも分からない状態でブロックを処理させっぱなしになってしまいます。
グループというのを使えば、asyncで処理する複数のブロックをひとつのグループにまとめて、それらのブロック全部が終わったタイミングを受け取る事が出来ます。
まず、グループを作る関数がこれです。
dispatch_group_t dispatch_group_create(void);
次に、グループで処理させたいブロックを渡すのがdispatch_group_asyncです。グループはasyncオンリーでsyncはありません。
void dispatch_group_async(dispatch_group_t group, dispatch_queue_t queue, dispatch_block_t block);
そして、グループの処理全部が終わったら呼ばれるブロックを設定するのがdispatch_group_notifyです。基本的に、グループ内共通で使ったメモリ領域などを解放するのに使うようです。どこかに通知するのにも使えると思います。
void dispatch_group_notify(dispatch_group_t group, dispatch_queue_t queue, dispatch_block_t block);
カレントスレッドで処理の終わりを待つ必要があれば、待たせることもできます。dispatch_group_waitです。どれだけ待つのかを指定できます。DISPATCH_TIME_FOREVERという定数をtimeoutに渡せば、いつまでも待ちます。
long dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout);
なお、waitで待っても待たなくても、notifyで設定したブロックが呼ばれるのは別スレッドのようです。
サンプルソースです。
#include <stdio.h> #include <dispatch/dispatch.h> int main (int argc, const char * argv[]) { dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_group_t group = dispatch_group_create(); __block int sum = 0; for (int i = 0; i < 10; i++) { dispatch_group_async(group, queue, ^{ printf("+%d\n", i); sum += i; }); } dispatch_group_notify(group, queue, ^{ printf("finish"); //<strike>dispatch_release(group);</strike> }); dispatch_group_wait(group, DISPATCH_TIME_FOREVER); printf("sum = %d\n", sum); dispatch_release(group); return 0; }
前回のapplyと同じような処理をさせています。waitが無ければ、おそらくsumは0で終わってしまうと思いますが、waitで待っている事でちゃんと45と答えが出ます。
waitの代わりにdispatch_main()にしておいてnotifyのブロックの中でsumを見れば、そこでも45となっていて、ちゃんとasyncで渡したブロックが全て実行された後に呼ばれる事が分かると思います。
ちなみに、notifyのブロックが呼ばれる前にasyncで新たなブロックを同じグループに入れると、その新しいブロックも終わってからnotifyのブロックが呼ばれます。が、、notifyのタイミングが新たなブロックの前か後かどちらでくるか分からなくなってしまうので、やらない方が良いと思います。
dispatch_group_notify に dispatch_release を突っ込むと、
この場合、低頻度ですが dispatch_group_wait に辿り着く前に group が解放され、dispatch_group_wait の箇所で、エラーが発生しますね。
dispatch_group_notify に dispatch_release は入れない方が吉。
ご指摘ありがとうございます。dispatch_releaseをdispatch_group_waitの後に修正しました。