dispatch_sync в основной очереди зависает в модульном тесте

У меня были некоторые проблемы с модульным тестированием какого-то грандиозного кода центральной диспетчеризации с помощью встроенной платформы модульного тестирования XCode, SenTestingKit. Мне удалось сварить мою проблему с этим. У меня есть модульный тест, который строит блок и пытается выполнить его в главном потоке. Однако блок фактически никогда не выполняется, поэтому тест зависает, потому что это синхронная отправка.

- (void)testSample {

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

    STFail(@"FAIL!");
}

Что в среде тестирования вызывает зависание?

5 ответов

Решение

dispatch_sync запускает блок в данной очереди и ожидает его завершения. В этом случае очередь является основной очередью отправки. Основная очередь выполняет все свои операции в главном потоке в порядке FIFO (первым пришел-первым обслужен). Это означает, что всякий раз, когда вы звоните dispatch_sync Ваш новый блок будет помещен в конец строки и не будет работать до тех пор, пока все остальное в очереди не будет выполнено.

Проблема здесь в том, что блок, который вы только что поместили в очередь, находится в конце строки, ожидающей запуска в главном потоке, в то время как testSample В настоящее время метод работает в главном потоке. Блок в конце очереди не может получить доступ к основному потоку, пока текущий метод (сам) не завершит использование основного потока. тем не мение dispatch_sync означает, что отправляет объект блока для выполнения в очередь отправки и ожидает, пока этот блок не завершится.

Проблема в вашем коде в том, что независимо от того, используете ли вы dispatch_sync или же dispatch_async, STFail() всегда будет вызываться, в результате чего ваш тест не пройден.

Что еще более важно, как объяснил Б. Дж. Гомер, если вам нужно что-то запустить синхронно в основной очереди, вы должны убедиться, что вы не в основной очереди, иначе произойдет тупик. Если вы находитесь в основной очереди, вы можете просто запустить блок как обычную функцию.

Надеюсь это поможет:

- (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!");
}

Если вы находитесь в главной очереди и синхронно ждете, когда основная очередь станет доступной, вы действительно будете ждать долго. Вы должны проверить, чтобы убедиться, что вы еще не в основном потоке.

Будете ли вы когда-нибудь выходить из дома, если вы должны подождать, пока вы сами выйдете из дома? Вы угадали! Нет!:]

В основном если:

  1. Вы находитесь на FooQueue. (не должно быть main_queue)
  2. Вы вызываете метод, используя sync т.е. в последовательном порядке и хотите выполнить на FooQueue.

Это никогда не произойдет по той же причине, по которой вы никогда не выберетесь из дома!

Это никогда не будет отправлено, потому что это должно ждать себя, чтобы выйти из очереди!

Чтобы следить, так как

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!");
}
Другие вопросы по тегам