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

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

- (void)testSample {

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

    STFail(@"FAIL!");
}

Что такого в тестовой среде, что заставляет это зависать?


person Drewsmits    schedule 19.10.2011    source источник
comment
Хороший вопрос, и я с нетерпением жду правильного ответа. Я несколько раз обнаруживал, что использование dispatch_sync в основной очереди приводит к тупиковой ситуации, поэтому я просто избегаю этого в целом.   -  person D.C.    schedule 19.10.2011
comment
@ОКРУГ КОЛУМБИЯ. несколько раз или ВСЕГДА? Мне любопытно, как вы можете dispatch_sync(dispatch_get_main_queue() , пока в основном потоке не создадите мертвую блокировку !?   -  person Honey    schedule 27.12.2016


Ответы (5)


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

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

person BJ Homer    schedule 19.10.2011
comment
Прекрасный. Вторая благодарность. У меня есть код, который я хочу принудительно запустить в основном потоке, так что это похоже на способ сделать это. Но (а) как я могу узнать, нахожусь ли я в каком-то другом потоке и (б) как мой код, не являющийся основным потоком, вообще попал туда? Любые подсказки приветствуются :) - person Tim Erickson; 27.04.2014
comment
Будет ли это рассматриваться как пример взаимоблокировки? - person Pescolly; 01.11.2017

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

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

person NJones    schedule 19.10.2011

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

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

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

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

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

person Honey    schedule 24.05.2016

Чтобы продолжить, поскольку

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!");
}
person codebaboon    schedule 24.04.2015
comment
Нахождение в основном потоке не гарантирует, что вы работаете в основной очереди. Это разные вещи. Основная очередь выполняется в основном потоке, но могут быть и другие очереди, работающие в ней. - person Alejandro Iván; 13.04.2017
comment
Можете ли вы обосновать это заявление какой-либо документацией или чем-то еще? @ AlejandroIván Более того, даже несмотря на то, что вы говорите, этот код должен быть безопасным. Поскольку основная очередь может выполняться только в основном потоке, и мы, очевидно, не находимся в основном потоке, когда выполняется предложение else. Таким образом, синхронизация с основной очередью никогда не должна создавать проблемы или тупиковые ситуации. - person emrepun; 14.05.2020
comment
@emrepun, если вы попадаете в условие if (я имею в виду, вы не попадаете в else), вы не можете быть уверены, что находитесь в основной очереди, только в основном потоке (где выполняется основная очередь). Посмотрите эту запись в блоге: blog.benjamin-encz.de / post / main-queue-vs-main-thread - person Alejandro Iván; 15.05.2020