GCD для выполнения задачи в главном потоке

255

У меня есть обратный вызов, который может прийти из любого потока. Когда я получаю этот обратный вызов, я хотел бы выполнить определенную задачу в главном потоке.

Нужно ли проверять, нахожусь ли я уже в главном потоке - или есть какой-либо штраф, если не выполнить эту проверку до вызова кода ниже?

dispatch_async(dispatch_get_main_queue(), ^{
   // do work here
});
Эгиль
источник
117
Пять лет спустя я до сих пор не могу вспомнить синтаксис блоков GCD и оказываться здесь каждый раз.
SpaceTrucker
7
@SpaceTrucker - по той же причине, что и я на этой странице: D
Мэтью Коули
4
9 лет спустя, и я все еще пришел, чтобы скопировать синтаксис с этой страницы.
Оса

Ответы:

153

Нет, вам не нужно проверять, находитесь ли вы уже в главном потоке. Отправляя блок в основную очередь, вы просто планируете последовательное выполнение блока в главном потоке, что происходит при выполнении соответствующего цикла выполнения.

Если вы уже находитесь в главном потоке, поведение такое же: блок запланирован и выполняется при запуске цикла выполнения основного потока.


источник
3
Вопрос был в том, есть ли «штраф за невыполнение этой проверки» ... Я думаю, что существует потеря производительности за использование асинхронной диспетчеризации, когда она не нужна, или это тривиально?
Дэн Розенстарк
1
@ Яр Я не думаю, что в большинстве случаев заметно влияние на производительность: GCD - легковесная библиотека. Тем не менее, я понял вопрос как: «учитывая приведенный ниже код, нужно ли мне проверять, нахожусь ли я в главном потоке?»
7
Однако вы ДОЛЖНЫ проверить, используете ли вы dispatch_sync. В противном случае вы получите тупик.
Виктор Энгель
Если вы находитесь в главной очереди и отправляете asyncобратно в основную очередь, она будет запущена, но это может испортить ожидаемое время ваших действий . Такие , как код пользовательского интерфейса в viewDidLoad() не работает до тех пор , после того, как сначала отображается вид .
Пкамб
106

Для случая асинхронной отправки, который вы описали выше, вам не нужно проверять, находитесь ли вы в главном потоке. Как указывает Bavarious, он просто будет поставлен в очередь для запуска в главном потоке.

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

void runOnMainQueueWithoutDeadlocking(void (^block)(void))
{
    if ([NSThread isMainThread])
    {
        block();
    }
    else
    {
        dispatch_sync(dispatch_get_main_queue(), block);
    }
}

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

runOnMainQueueWithoutDeadlocking(^{
    //Do stuff
});
Брэд Ларсон
источник
1
Почему бы не назвать это чем-то вроде «runOnMainQueueSync»? Тот факт, что он может зайти в тупик, а это не так, я бы не хотел, чтобы во всем моем коде. Спасибо и +1 как всегда.
Дэн Розенстарк
2
@ Яр - я просто дал ему это имя, чтобы мне было понятно, почему я не просто синхронно запускаю блоки в главной очереди, а не использую эту вспомогательную функцию. Это было больше напоминанием для меня.
Брэд Ларсон
3
С помощью этой функции все еще можно заблокировать. Синхронизация основной очереди> синхронизация другой очереди> основная очередь блокируется.
hfossli
2
@hfossli - Правда, это не будет обрабатывать каждый случай. В моем случае я всегда звонил из асинхронной отправки в фоновую последовательную очередь или встроенным кодом в главном потоке. Интересно dispatch_set_specific(), поможет ли это в случае, который вы описываете: stackoverflow.com/a/12806754/19679 .
Брэд Ларсон
1
[NSThread isMainThread] часто возвращает YES и не считается безопасным для проверки этого случая в программировании GCD. stackoverflow.com/questions/14716334/…
Уилл Ларш
57

Как уже упоминалось в других ответах, dispatch_async из основного потока в порядке.

Однако, в зависимости от вашего варианта использования, есть побочный эффект, который вы можете рассматривать как недостаток: поскольку блок запланирован в очереди, он не будет выполняться до тех пор, пока управление не вернется в цикл выполнения, что приведет к задержке выполнение вашего блока.

Например,

NSLog(@"before dispatch async");
dispatch_async(dispatch_get_main_queue(), ^{
    NSLog(@"inside dispatch async block main thread from main thread");
});
NSLog(@"after dispatch async");

Распечатает:

before dispatch async
after dispatch async
inside dispatch async block main thread from main thread

По этой причине, если вы ожидаете, что блок будет выполняться между внешними NSLog, dispatch_async не поможет вам.

Майкл Чинен
источник
Надо сказать, что это важно учитывать
Марк-Александр Берубе
Поскольку вы хотите проверить, это означает, что код может выполняться или не выполняться в основном потоке. Уже в основном потоке будет работать в таком порядке, в противном случае это не может быть гарантировано. Поэтому вам следует избегать проектирования кода для запуска в определенном порядке, когда речь идет о многопоточном программировании. Попробуйте использовать асинхронный обратный вызов.
оупс
1

Нет, вам не нужно проверять, находитесь ли вы в главном потоке. Вот как вы можете сделать это в Swift:

runThisInMainThread { () -> Void in
    runThisInMainThread { () -> Void in
        // No problem
    }
}

func runThisInMainThread(block: dispatch_block_t) {
    dispatch_async(dispatch_get_main_queue(), block)
}

Он включен в качестве стандартной функции в моем репо, проверьте его: https://github.com/goktugyil/EZSwiftExtensions

Esqarrouth
источник