iPhone - головна нитка Grand Central Dispatch


145

Я з успіхом використовував грандіозну центральну розсилку в своїх додатках, але мені було цікаво, яка реальна перевага використання чогось подібного:

dispatch_async(dispatch_get_main_queue(), ^{ ... do stuff

або навіть

dispatch_sync(dispatch_get_main_queue(), ^{ ... do stuff

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

[self doStuff];

правильно?

Цікаво, що ви думаєте, хлопці


9
До речі, закидання головної черги в dispatch_sync призведе до тупикової ситуації.
Брукс Ханес

5
Просто прочитайте його в документах: "На відміну від dispatch_async, [dispatch_sync] не повертається, поки блок не завершиться. Виклик цієї функції та націлювання на поточну чергу призводить до тупикової ситуації." ... Але, можливо, я читаю цю помилку ... ( поточна черга не означає основного потоку). Будь ласка, виправте, якщо я помиляюся.
Брукс Ханес

4
@BrooksHanes не завжди правда. Це призведе до тупикової ситуації, якщо ви вже перебуваєте на основній нитці. Якби не тоді, не було б глухого кута. Дивіться тут
мед

Відповіді:


296

Пересилання блоку в основну чергу, як правило, здійснюється з фонової черги, щоб сигналізувати про те, що деяка обробка фону закінчена, наприклад

- (void)doCalculation
{
    //you can use any string instead "com.mycompany.myqueue"
    dispatch_queue_t backgroundQueue = dispatch_queue_create("com.mycompany.myqueue", 0);

    dispatch_async(backgroundQueue, ^{
        int result = <some really long calculation that takes seconds to complete>;

        dispatch_async(dispatch_get_main_queue(), ^{
            [self updateMyUIWithResult:result];
        });    
    });
}

У цьому випадку ми робимо тривалий розрахунок у фоновій черзі і нам потрібно оновити наш інтерфейс користувача після завершення розрахунку. Оновлення користувальницького інтерфейсу, як правило, повинно здійснюватися з основної черги, тому ми "подаємо сигнал" до головної черги за допомогою другої вкладеної dispatch_async.

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

  • обробка фону завершена -> оновлення інтерфейсу користувача
  • фрагмент даних, оброблених у фоновій черзі -> сигнал основної черги, щоб почати наступний фрагмент
  • вхідні мережеві дані у фоновій черзі -> сигнал головної черги, що повідомлення надійшло
  • тощо

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


А, бачу. Отже, я маю рацію. Немає переваги в цьому, якщо ви вже перебуваєте на головній черзі, просто якщо ви перебуваєте на іншій черзі та хочете оновити інтерфейс користувача. Дякую.
Качка

Щойно відредагував мою відповідь, щоб поговорити про те, чому це не дуже корисно робити з головної черги.
Робін Саммерхілл

Крім того, я думаю, що в iOS 4 є помилка (можливо, вона зайшла в iOS 5), де dispatch_sync до основної черги з головної нитки просто викликає зависання, тому я б уникну цього не робити цілком.
joerick

10
Це не помилка, це очікувана поведінка. Не дуже корисна поведінка, правда, але вам завжди потрібно бути в курсі тупикових ситуацій, коли використовуєте dispatch_sync. Ви не можете очікувати, що система весь час захищатиме вас від помилок програміста.
Робін Саммерхілл

2
Яка тут черга? Як створити об'єкт backgroundQueue
Nilesh Tupe

16

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

Наприклад, ви можете написати по суті єдиний потоковий сервер, який, однак, обробляє багато одночасних з'єднань. Поки жоден окремий блок у черзі не забирає занадто багато часу, сервер залишається реагувати на нові запити.

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


11

Сподіваюсь, я правильно розумію ваше запитання в тому, що вам цікаво про відмінності між dispatch_async та dispatch_sync?

dispatch_async

буде асинхронно відправляти блок до черги. Це означає, що він буде відправляти блок у чергу і не чекати, коли він повернеться, перш ніж продовжувати виконувати код, що залишився у вашому методі.

dispatch_sync

синхронно відправить блок до черги. Це дозволить уникнути більше виконання залишеного коду в методі, поки блок не закінчить виконання.

Я в основному використовував dispatch_asyncфонову чергу, щоб отримати роботу з головної черги та скористатися будь-якими додатковими ядрами, які може мати пристрій. Потім dispatch_asyncдо основного потоку, якщо мені потрібно оновити інтерфейс користувача.

Удачі


1
дякую, але я запитую про переваги від того, щоб щось надсилати в основну чергу, перебуваючи на головній черзі.
Качка

9

Одне місце, де це корисно, - це діяльність із інтерфейсом користувача, наприклад встановлення спінера перед тривалою операцією:

- (void) handleDoSomethingButton{

    [mySpinner startAnimating];

    (do something lengthy)
    [mySpinner stopAnimating];
}

не вийде, тому що ви блокуєте основну нитку під час своєї тривалої речі і не даєте UIKit насправді запустити спінер.

- (void) handleDoSomethingButton{
     [mySpinner startAnimating];

     dispatch_async (dispatch_get_main_queue(), ^{
          (do something lengthy)
          [mySpinner stopAnimating];
    });
}

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


@Jerceratops так, але це дозволяє поточному циклу запуску.
Дан Розенштарк

3
Так, але все одно страшно. Він все ще блокує інтерфейс користувача. Я можу натиснути іншу кнопку відразу після цієї. Або спробуйте прокрутити. "(зробіть щось тривале)" не повинно відбуватися на головному потоці, а dispatch_async, щоб кнопка натискала кнопку "закінчити", не є прийнятним рішенням.
Jerceratops


1

Async означає асинхронність, і ви повинні використовувати це більшу частину часу. Ніколи не слід викликати синхронізацію в основному потоці, оскільки вона заблокує ваш інтерфейс користувача, поки завдання не буде виконано. Ви Ось кращий спосіб зробити це в Swift:

runThisInMainThread { () -> Void in
    // Run your code like this:
    self.doStuff()
}

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.