200

GCD を使用する場合、次の実行ステップに進む前に、2 つの非同期ブロックが実行されて完了するまで待機する必要があります。それを行う最善の方法は何ですか?

次のことを試しましたが、うまくいかないようです。

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
    // block1
});


dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
    // block2
});

// wait until both the block1 and block2 are done before start block3
// how to do that?

dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
    // block3
});
4

10 に答える 10

318

ディスパッチ グループを使用します。例について、Apple の iOS Developer Library の同時実行プログラミング ガイドの「ディスパッチ キュー」の章にある「キューに入れられたタスクのグループでの待機」を参照してください。

例は次のようになります。

dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
    // block1
    NSLog(@"Block1");
    [NSThread sleepForTimeInterval:5.0];
    NSLog(@"Block1 End");
});


dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
    // block2
    NSLog(@"Block2");
    [NSThread sleepForTimeInterval:8.0];
    NSLog(@"Block2 End");
});

dispatch_group_notify(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
    // block3
    NSLog(@"Block3");
});

// only for non-ARC projects, handled automatically in ARC-enabled projects.
dispatch_release(group);

次のような出力を生成できます。

2012-08-11 16:10:18.049 Dispatch[11858:1e03] Block1
2012-08-11 16:10:18.052 Dispatch[11858:1d03] Block2
2012-08-11 16:10:23.051 Dispatch[11858:1e03] Block1 End
2012-08-11 16:10:26.053 Dispatch[11858:1d03] Block2 End
2012-08-11 16:10:26.054 Dispatch[11858:1d03] Block3
于 2012-08-10T22:05:16.537 に答える
282

dispatch_async非同期完了ブロックの場合のように、ブロックの呼び出しを制御できない場合は、Jörn Eyrich の回答を拡張します (これに賛成する場合は彼の回答に賛成します)、dispatch_group_enterおよびを使用して GCD グループをdispatch_group_leave直接使用できます。

この例では、computeInBackground変更できないもの (デリゲート コールバック、NSURLConnection の completionHandler などであると想像してください) を装っているため、ディスパッチ呼び出しにアクセスできません。

// create a group
dispatch_group_t group = dispatch_group_create();

// pair a dispatch_group_enter for each dispatch_group_leave
dispatch_group_enter(group);     // pair 1 enter
[self computeInBackground:1 completion:^{
    NSLog(@"1 done");
    dispatch_group_leave(group); // pair 1 leave
}];

// again... (and again...)
dispatch_group_enter(group);     // pair 2 enter
[self computeInBackground:2 completion:^{
    NSLog(@"2 done");
    dispatch_group_leave(group); // pair 2 leave
}];

// Next, setup the code to execute after all the paired enter/leave calls.
//
// Option 1: Get a notification on a block that will be scheduled on the specified queue:
dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
    NSLog(@"finally!");
});

// Option 2: Block an wait for the calls to complete in code already running
// (as cbartel points out, be careful with running this on the main/UI queue!):
//
// dispatch_group_wait(group, DISPATCH_TIME_FOREVER); // blocks current thread
// NSLog(@"finally!");

この例では、computeInBackground:completion: は次のように実装されています。

- (void)computeInBackground:(int)no completion:(void (^)(void))block {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        NSLog(@"%d starting", no);
        sleep(no*2);
        block();
    });
}

出力 (実行からのタイムスタンプ付き):

12:57:02.574  2 starting
12:57:02.574  1 starting
12:57:04.590  1 done
12:57:06.590  2 done
12:57:06.591  finally!
于 2014-01-03T18:47:57.497 に答える
58

別の GCD の代替案はバリアです。

dispatch_queue_t queue = dispatch_queue_create("com.company.app.queue", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, ^{ 
    NSLog(@"start one!\n");  
    sleep(4);  
    NSLog(@"end one!\n");
});

dispatch_async(queue, ^{  
    NSLog(@"start two!\n");  
    sleep(2);  
    NSLog(@"end two!\n"); 
});

dispatch_barrier_async(queue, ^{  
    NSLog(@"Hi, I'm the final block!\n");  
});

同時キューを作成し、2 つのブロックをディスパッチしてから、最後のブロックをバリア付きでディスパッチします。これにより、残りの 2 つのブロックが完了するまで待機します。

于 2013-10-25T03:27:45.300 に答える
40

あなたが GCD について尋ねたことは知っていますが、必要に応じて、NSOperationQueueこの種のものを本当に優雅に処理します。

NSOperationQueue *queue = [[NSOperationQueue alloc] init];

NSOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"Starting 3");
}];

NSOperation *operation;

operation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"Starting 1");
    sleep(7);
    NSLog(@"Finishing 1");
}];

[completionOperation addDependency:operation];
[queue addOperation:operation];

operation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"Starting 2");
    sleep(5);
    NSLog(@"Finishing 2");
}];

[completionOperation addDependency:operation];
[queue addOperation:operation];

[queue addOperation:completionOperation];
于 2013-01-26T19:43:03.707 に答える
3

最初の答えは本質的に正しいですが、目的の結果を達成するための最も簡単な方法が必要な場合は、セマフォを使用してそれを行う方法を示すスタンドアロンのコード例を次に示します (これは、ディスパッチ グループが舞台裏でどのように機能するか、JFYI でもあります)。 :

#include <dispatch/dispatch.h>
#include <stdio.h>

main()
{
        dispatch_queue_t myQ = dispatch_queue_create("my.conQ", DISPATCH_QUEUE_CONCURRENT);
        dispatch_semaphore_t mySem = dispatch_semaphore_create(0);

        dispatch_async(myQ, ^{ printf("Hi I'm block one!\n"); sleep(2); dispatch_semaphore_signal(mySem);});
        dispatch_async(myQ, ^{ printf("Hi I'm block two!\n"); sleep(4); dispatch_semaphore_signal(mySem);});
        dispatch_async(myQ, ^{ dispatch_semaphore_wait(mySem, DISPATCH_TIME_FOREVER); printf("Hi, I'm the final block!\n"); });
        dispatch_main();
}
于 2012-08-11T22:01:15.513 に答える
-4

Not to say other answers are not great for certain circumstances, but this is one snippet I always user from Google:

- (void)runSigninThenInvokeSelector:(SEL)signInDoneSel {


    if (signInDoneSel) {
        [self performSelector:signInDoneSel];
    }

}
于 2016-05-08T06:56:34.660 に答える