GCD для виконання завдання в основному потоці


254

У мене є зворотний дзвінок, який може надходити з будь-якої нитки. Коли я отримую цей зворотний дзвінок, я хотів би виконати певне завдання на головній нитці.

Чи потрібно перевіряти, чи вже я в основному потоці - чи є пені, якщо я не виконую цю перевірку befora, викликаючи код нижче?

dispatch_async(dispatch_get_main_queue(), ^{
   // do work here
});

116
П'ять років потому я все ще не можу запам’ятати синтаксис блоків GCD і закінчуватися тут кожного разу.
SpaceTrucker

7
@SpaceTrucker - То ж саме, що я на цій сторінці: D
Меттью Каулі

4
Через 9 років, і я все ще приходжу скопіювати синтаксис з цієї сторінки.
Оса

Відповіді:


152

Ні, вам не потрібно перевіряти, чи вже ви в основному потоці. Відправляючи блок до основної черги, ви просто плануєте блок, який потрібно послідовно виконувати на головному потоці, що відбувається, коли виконується відповідний цикл запуску.

Якщо ви вже перебуваєте на головному потоці, поведінка однакова: блок планується та виконується, коли цикл запуску основного потоку виконується.


3
Питання полягало в тому, чи існує "штраф за невиконання цієї перевірки" ... Я б подумав, що існує штраф за ефективність використання асинхронної диспетчери, коли це не потрібно, або це тривіально?
Дан Розенстарк

1
@Yar Я не думаю, що в більшості випадків помітний вплив на продуктивність: GCD - це легка бібліотека. У зв'язку з цим, я зрозумів питання як: "Враховуючи код, чи потрібно мені перевіряти, чи я в основному потоці?

7
Однак ви повинні перевірити, чи використовуєте ви dispatch_sync. Інакше ви отримаєте тупик.
Віктор Енгель

Якщо ви перебуваєте в основній черзі і відправляєтесь asyncназад на головну чергу, вона буде запущена, але це може зіпсувати очікуваний час ваших дій . Такий, як код інтерфейсу, viewDidLoad() не працює до того моменту, коли вперше відобразиться перегляд .
pkamb

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
@Yar - Я просто дав йому це ім'я, щоб мені було зрозуміло, чому я не просто знімаю блоки синхронно на головній черзі, а не використовую цю помічну функцію. Це було більше нагадуванням про себе.
Бред Ларсон

3
Досі можлива тупикова ситуація за допомогою цієї функції. Синхронізація основної черги> синхронізація інших черг> основна черга буде тупиковою.
hfossli

2
@hfossli - Правда, вона не оброблятиме кожен випадок. У моєму використанні я завжди або телефонував з асинхронної відправки на фоновій послідовній черзі, або з вбудованого коду в основний потік. Цікаво, чи dispatch_set_specific()допоможе вам у випадку, який ви описуєте: stackoverflow.com/a/12806754/19679 .
Бред Ларсон

1
[NSThread isMainThread] часто повертає ТАК і не вважається безпечним для перевірки цього випадку в програмуванні 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 не допоможе вам.


Необхідно сказати, що це важливо враховувати
Марк-Олександр Берубе

Оскільки ви хочете перевірити, це означає, що код може або не може працювати в основному потоці. Вже в основному потоці буде працювати в тому порядку, інакше цього не можна гарантувати. Тож вам слід уникати проектування коду для виконання у певному порядку, коли йдеться про багатопотокове програмування. Спробуйте скористатися способом зворотного виклику async.
ooops

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.