LoginSignup
42
41

More than 5 years have passed since last update.

dispatch_group_tと非同期処理の終了待機

Last updated at Posted at 2016-01-05

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
42
41
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
42
41