33

私はGCDで遊んでいて、おもちゃのCoinFlipperアプリを作成しました。

コインを裏返す方法は次のとおりです。

- (void)flipCoins:(NSUInteger)nFlips{

    // Create the queues for work
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL);

    // Split the number of flips into whole chunks of kChunkSize and the remainder.
    NSUInteger numberOfWholeChunks = nFlips / kChunkSize;
    NSUInteger numberOfRemainingFlips = nFlips - numberOfWholeChunks * kChunkSize;

    if (numberOfWholeChunks > 0) {
        for (NSUInteger index = 0; index < numberOfWholeChunks; index++) {
            dispatch_async(queue, ^{
                NSUInteger h = 0;
                NSUInteger t = 0;
                flipTheCoins(kChunkSize, &h, &t);
                dispatch_async(mainQueue, ^{
                    self.nHeads += h;
                    self.nTails += t;
                });
            });
        }
    }
    if (numberOfRemainingFlips > 0) {
        dispatch_async(queue, ^{
            NSUInteger h = 0;
            NSUInteger t = 0;
            flipTheCoins(numberOfRemainingFlips, &h, &t);
            dispatch_async(mainQueue, ^{
                self.nHeads += h;
                self.nTails += t;
            });
        });

    }
}

ご覧のように; フリップの数を大きなチャンクに分割して、バックグラウンドでフリップし、メインキューのプロパティを更新しています。プロパティはウィンドウコントローラーによって監視されており、UIは実行結果で更新されます。

同時実行プログラミングガイドとGCDドキュメントを確認しました。キューを一時停止する方法はありますが、それらを停止して、キューに入れられて実行されていないすべてのオブジェクトを削除する方法はありません。

「停止」ボタンを接続して、開始後に反転をキャンセルできるようにしたいと思います。これにより、プロパティをNSOperationQueue監視して、実行されているかどうかを確認し、キューに入れられたブロックを削除できます。operationCountcancelAllOperations

同時実行プログラミングガイドとGCDドキュメントを確認しました。キューを一時停止する方法はありますが、それらを停止して、キューに入れられて実行されていないすべてのオブジェクトを削除する方法はありません。

それで :-

  1. キューに追加したブロックがまだ待機しているかどうかを確認するにはどうすればよいですか?
  2. まだ実行されていないブロックをキャンセルするにはどうすればよいですか?
  3. 私はGCDに慣れていないので、正しくやっていますか?
4

3 に答える 3

49

これは、GCDでプログラミングする場合の半一般的な質問です。

簡単に言うと、GCDにはキュー用のキャンセルAPIがありません。理論的根拠:

  1. 特定のブロックが特定のメモリ割り当てを解放する役割を果たしている可能性があるため、メモリ管理は非常に複雑になります。GCDは常にブロックを実行することにより、メモリ管理を容易にします。
  2. 状態を壊さずに実行中のブロックを停止することは事実上不可能です。
  3. キャンセルロジックを必要とするほとんどのコードは、プライベートデータ構造でその状態をすでに追跡しています。

これらすべてのケースを考えると、次のようなコードを書く方がはるかに効率的で強力です。

dispatch_async(my_obj->queue, ^{
    bool done = false;
    // do_full_update() takes too long, therefore:
    while ( !my_obj->cancelled && !done ) {
        done = do_partial_update(my_obj);
    }
});

ああ、キューがエンキューされたすべてのブロックの実行を終了したかどうかを知るために、コードは同期APIを使用して空のブロックを実行するだけです。

dispatch_sync(my_obj->queue, ^{});

コメントで述べたように、作業がいつ完了したかを知るためのより良い方法は、ディスパッチグループを使用することです。すべてのブロックをグループにディスパッチしてから、完了ハンドラーをグループに追加できます。作業が完了すると、完了ブロックが実行されます。

dispatch_group_t myGroup = dispatch_group_create();
dispatch_group_async(myGroup, my_obj->queue, ^{
    bool done = false;
    while ( !my_obj->cancelled && !done ) {
        done = do_partial_update(my_obj);
    }
});
dispatch_group_notify(myGroup, my_obj->queue, ^{
    NSLog(@"Work is done!");
    dispatch_release(myGroup);
});

すべてのブロックが完了すると、グループは空になり、通知ブロックがトリガーされます。そこから、UIなどを更新できます。

頑張って楽しんでね!

于 2009-10-11T16:15:47.543 に答える
5

実行されているかどうかを確認する方法

BOOL dispatch_queue_is_empty(dispatch_queue_t queue)
{
    dispatch_group_t group = dispatch_group_create();

    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        dispatch_group_leave(group);
    });

    int64_t maxWaitTime = 0.00000005 * NSEC_PER_SEC;
    BOOL isReady = dispatch_group_wait(group, maxWaitTime) == 0;

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
        dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
        dispatch_release(group);
    });

    return isReady;
}

アプリでテストするには

dispatch_queue_t queue = dispatch_queue_create("test", 0);

NSLog(@"Is empty %@", dispatch_queue_is_empty(queue) ? @"YES" : @"NO");

dispatch_async(queue, ^{
    for(int i = 0; i < 100; i++)
    {
        NSLog(@"... %i", i);
    }
});

NSLog(@"Is empty %@", dispatch_queue_is_empty(queue) ? @"YES" : @"NO");

結果

Is empty YES
Is empty NO
... 0
... 1
... 2
... 3
... 4
... 5
... 6
... 7
... 8
... 9

変数のデフォルト値は、必要なmaxWaitTime結果に調整できます。

于 2013-10-14T17:31:27.030 に答える
2

シリアルディスパッチキューまたはコンカレントディスパッチキューがある場合、同じことを実行できるコードを次に示します。

BOOL __block queueIsEmpty = false;
dispatch_barrier_async (_dispatchQueue, ^{
    queueIsEmpty = true;
});

while (!queueIsEmpty) {
    int i = 0;  // NOOP instruction
}

// At this point your queue should be empty.
于 2014-07-17T22:02:22.510 に答える