Розуміння dispatch_async


233

У мене є питання щодо цього коду

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSData* data = [NSData dataWithContentsOfURL: 
      kLatestKivaLoansURL];
    [self performSelectorOnMainThread:@selector(fetchedData:) 
      withObject:data waitUntilDone:YES];
});

Перший параметр цього коду

dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) 

Чи ми просимо цей код виконувати послідовні завдання на глобальній черзі, визначення яких саме полягає в тому, що він повертає глобальну паралельну чергу заданого рівня пріоритету?

Яка перевага використання dispatch_get_global_queueнад основною чергою?

Я збентежений. Не могли б ви допомогти мені зрозуміти це краще.


1
Вам слід краще скоротити код на кілька рядків, щоб він мав більше сенсу. захистити свій dispatch_get_global_queueвнутрішній змінний тип dispatch_queue_t myQueue. Її легше читати, передаючи лише мою чергу до вашого `` dispatch_async`
Алекс Ціо

Відповіді:


517

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

Наприклад, якщо я завантажую файл з Інтернету і хочу оновити користувача про хід завантаження, я запустити завантаження в черзі пріоритетних пріоритетів і оновити інтерфейс користувача в основній черзі асинхронно.

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
    //Background Thread
    dispatch_async(dispatch_get_main_queue(), ^(void){
        //Run UI Updates
    });
});

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

Я роблю саме те, що ви пропонуєте, але якось, uiTableViewCell не оновляється відразу, коли я викликаю [self.tableView reloadData] в Оновленнях інтерфейсу Run. Це займає близько 4 або 5 секунд. Він зводить мене з розуму вже кілька днів .
GrandSteph

@GrandSteph Я не надто знайомий з цим методом. Можливо, для цього методу потрібно лише 5 секунд. Найважливіша річ у dispatch_async - це те, що вона дозволяє робити речі у фоновому режимі без підвішування основної нитки.
Девід

2
що 0означає?
Мед

3
@Honey 0 - це flagsпараметр, який наразі не робить нічого. З Документів:Flags that are reserved for future use. Always specify 0 for this parameter.
Девід

199

Усі черги DISPATCH_QUEUE_PRIORITY_X - це паралельні черги (це означає, що вони можуть виконувати відразу кілька завдань) і є FIFO в тому сенсі, що завдання в межах певної черги почнуть виконуватись, використовуючи порядок "спочатку в, перший - з". Це порівняно з основною чергою (від dispatch_get_main_queue ()), яка є послідовною чергою (завдання почнуть виконувати та закінчувати виконання у тому порядку, у якому вони отримані).

Отже, якщо ви надсилаєте 1000 блоків dispatch_async () на DISPATCH_QUEUE_PRIORITY_DEFAULT, ці завдання почнуть виконуватись у тому порядку, коли ви їх надіслали в чергу. Аналогічно для черг ВИСОКИЙ, НИЗЬКИЙ та НАЗАД. Все, що ви надсилаєте в будь-яку з цих черг, виконується у фоновому режимі на альтернативних потоках, подалі від вашого основного потоку програми. Тому ці черги підходять для виконання таких завдань, як завантаження фону, стиснення, обчислення тощо.

Зауважте, що порядок виконання є FIFO на основі черги. Отже, якщо ви надсилаєте 1000 завдань dispatch_async () на чотири різні паралельні черги, рівномірно розділяючи їх та надсилаючи їх на BACKGROUND, LOW, DEFAULT та HIGH (наприклад, ви запланували останні 250 завдань на HIGH черзі), дуже ймовірно, що перші завдання, які ви бачите, починатимуться з цієї ВИСОКОЇ черги, оскільки система зробила ваші наслідки, що ці завдання потрібно якомога швидше потрапити до центрального процесора.

Зауважте також, що я кажу, що "почне виконувати по порядку", але майте на увазі, що як паралельні черги речі не обов'язково БЕЗКОШТОВНО виконувати в порядку залежно від тривалості часу для кожного завдання.

Відповідно до Apple:

https://developer.apple.com/library/content/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html

Паралельна черга відправки корисна, коли у вас є кілька завдань, які можуть працювати паралельно. Паралельна черга все ще є чергою, оскільки вона виконує завдання в порядку "перший-у-першому"; однак паралельна черга може видалити додаткові завдання до завершення попередніх завдань. Фактична кількість завдань, виконаних паралельною чергою в будь-який момент, є змінною і може динамічно змінюватися в міру зміни умов вашої програми. На кількість завдань, що виконуються паралельними чергами, впливає безліч факторів, включаючи кількість наявних ядер, обсяг роботи, що виконується іншими процесами, а також кількість та пріоритетність завдань в інших послідовних чергових відправленнях.

В основному, якщо ви надішлите ці 1000 блоків dispatch_async () до черги DEFAULT, HIGH, LOW або BACKGROUND, всі вони почнуть виконуватись в порядку, коли ви їх надсилаєте. Однак коротші завдання можуть закінчуватися і перед більш довгими. Причини цього полягають у тому, що є наявні ядра процесора або якщо поточні завдання черги виконують обчислювально неінтенсивну роботу (таким чином, система думає, що може паралельно відправляти додаткові завдання незалежно від кількості ядер).

Рівень одночасності повністю управляється системою і базується на навантаженні системи та інших внутрішньо визначених факторах. Це краса Grand Central Dispatch (системи dispatch_async ()) - ви просто робіть свої робочі одиниці як кодові блоки, встановлюєте пріоритет для них (виходячи з обраної вами черги) і дозволяєте системі обробляти решту.

Отже, щоб відповісти на ваше вище запитання: ви частково правильні. Ви "запитуєте цей код" для виконання одночасних завдань у глобальній паралельній черзі на визначеному рівні пріоритету. Код у блоці буде виконуватись у фоновому режимі, а будь-який додатковий (подібний) код буде виконуватися потенційно паралельно залежно від оцінки системи наявних ресурсів.

"Основна" черга з іншого боку (від dispatch_get_main_queue ()) - це послідовна черга (не є одночасною). Завдання, надіслані до головної черги, завжди виконуватимуться в порядку та завжди закінчуватимуться в порядку. Ці завдання також виконуватимуться в потоці користувальницького інтерфейсу, тому він підходить для оновлення вашого інтерфейсу повідомленнями про хід роботи, повідомленнями про завершення тощо.


+1, але я думаю, що на практиці не має великого значення, чи є паралельні черги FIFO чи просто випадковим порядком. Якщо ви запускаєте 5 завдань у циклі, припустіть, що вони по суті розпочнуться одночасно. Немає гарантії, що, наприклад, перша операція вводу / виводу 1-го завдання відбудеться до 5-ї, навіть якщо вони виконують один і той же код. OTOH, для послідовних черг поведінка FIFO є важливою, і IMHO - це визначальна різниця між двома типами черги.
Герхард Весп

Інкредиле пояснення. Багато плескає!
Охан Окбай

36

Швидка версія

Це швидка версія відповіді David's Objective-C. Ви використовуєте глобальну чергу для запуску речей у фоновому режимі, а основну чергу для оновлення інтерфейсу користувача.

DispatchQueue.global(qos: .background).async {
    
    // Background Thread
    
    DispatchQueue.main.async {
        // Run UI Updates
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.