dispatch_sync в основной очереди зависает в модульном тесте - PullRequest
39 голосов
/ 19 октября 2011

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

- (void)testSample {

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

    STFail(@"FAIL!");
}

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

Ответы [ 5 ]

104 голосов
/ 19 октября 2011

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

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

8 голосов
/ 19 октября 2011

Проблема в вашем коде в том, что независимо от того, используете ли вы 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!");
}
6 голосов
/ 19 октября 2011

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

2 голосов
/ 25 мая 2016

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

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

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

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

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

2 голосов
/ 24 апреля 2015

Следить, начиная с

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