web-dev-qa-db-ja.com

メインキューのdispatch_syncが単体テストでハングする

組み込みのXcodeユニットテストフレームワークであるSenTestingKitを使用して、大規模な中央ディスパッチコードのユニットテストで問題が発生していました。私はなんとか私の問題をこれで煮詰めました。ブロックをビルドしてメインスレッドで実行しようとする単体テストがあります。ただし、ブロックが実際に実行されることはないため、同期ディスパッチであるため、テストはハングします。

- (void)testSample {

    dispatch_sync(dispatch_get_main_queue(), ^(void) {
        NSLog(@"on main thread!");
    });

    STFail(@"FAIL!");
}

これがハングする原因となっているテスト環境についてはどうですか?

43
Drewsmits

_dispatch_sync_は、指定されたキューでブロックを実行し、ブロックが完了するのを待ちます。この場合、キューはメインのディスパッチキューです。メインキューは、すべての操作をメインスレッドでFIFO(先入れ先出し)の順序で)実行します。つまり、_dispatch_sync_を呼び出すたびに、新しいブロックが行の最後に置かれ、キュー内のそれが完了する前に他のすべてが完了するまで実行されません。

ここでの問題は、キューに入れたばかりのブロックが、testSampleメソッドの間、メインスレッドでの実行を待機している行のendにあることですis現在メインスレッドで実行されています。キューの最後にあるブロックは、現在のメソッド(それ自体)がメインスレッドを使用してを完了するまで、メインスレッドにアクセスできません。ただし、_dispatch_sync_は、ディスパッチキューで実行するためにブロックオブジェクトを送信し、そのブロックが完了するまで待機することを意味します。

111
BJ Homer

コードの問題は、_dispatch_sync_を使用しても_dispatch_async_を使用しても、STFail()が常に呼び出され、テストが失敗することです。

さらに重要なことに、BJ Homerが説明したように、メインキューで同期的に何かを実行する必要がある場合は、メインキューにいないことを確認する必要があります。そうしないと、デッドロックが発生します。メインキューにいる場合は、ブロックを通常の関数として実行できます。

お役に立てれば:

_- (void)testSample {

    __block BOOL didRunBlock = NO;
    void (^yourBlock)(void) = ^(void) {
        NSLog(@"on main queue!");
        // Probably you want to do more checks here...
        didRunBlock = YES;
    };

    // 2012/12/05 Note: dispatch_get_current_queue() function has been
    // deprecated starting in iOS6 and OSX10.8. Docs clearly state they
    // should be used only for debugging/testing. Luckily this is our case :)
    dispatch_queue_t currentQueue = dispatch_get_current_queue();
    dispatch_queue_t mainQueue = dispatch_get_main_queue();

    if (currentQueue == mainQueue) {
        blockInTheMainThread();
    } else {
        dispatch_sync(mainQueue, yourBlock); 
    }

    STAssertEquals(YES, didRunBlock, @"FAIL!");
}
_
8
nacho4d

メインキューにいて、メインキューが使用可能になるまで同期的に待機する場合、実際には長時間待機します。まだメインスレッドになっていないことを確認するためにテストする必要があります。

6
NJones

待機自分が最初に家から出る必要がある場合、あなたは家から出るでしょうか?正解です!番号! :]

基本的に:

  1. あなたはFooQueue上です。 (main_queueである必要はありません)
  2. syncを使用してメソッドを呼び出す、つまりシリアル方式でFooQueueで実行したい。

あなたが家から出られないのと同じ理由でそれは決して起こりません!

キューから降りるのを待つ必要があるため、ディスパッチされません。

3
Honey

フォローアップしてから

dispatch_get_current_queue()

は非推奨になりました。使用できます

[NSThread isMainThread]

メインスレッドにいるかどうかを確認します。

したがって、上記の他の答えを使用すると、次のことができます:

- (void)testSample 
{
    BOOL __block didRunBlock = NO;
    void (^yourBlock)(void) = ^(void) {
        NSLog(@"on main queue!");
        didRunBlock = YES;
    };

    if ([NSThread isMainThread])
        yourBlock();
    else
        dispatch_sync(dispatch_get_main_queue(), yourBlock);

    STAssertEquals(YES, didRunBlock, @"FAIL!");
}
2
codebaboon