Різниця між DispatchQueue.main.async та DispatchQueue.main.sync


99

Я вже давно використовую DispatchQueue.main.asyncдля виконання операцій, пов'язаних з інтерфейсом користувача.



Свіфт надає і те, і інше DispatchQueue.main.async і DispatchQueue.main.sync, і обидва виконуються в основній черзі.



Хтось може сказати мені різницю між ними? Коли слід використовувати кожну?



DispatchQueue.main.async {
    self.imageView.image = imageView
    self.lbltitle.text = ""

}

DispatchQueue.main.sync {
    self.imageView.image = imageView
    self.lbltitle.text = ""
}

Відповіді:


45

Коли ви використовуєте, asyncце дозволяє черзі викликів рухатися, не чекаючи, поки відправлений блок буде виконаний. Навпаки sync, черга виклику зупиниться і почекає, поки робота, яку ви відправили в блок, не буде закінчена. Тому syncце може призвести до тупикових ситуацій. Спробуйте бігтиDispatchQueue.main.sync з основної черги, і програма замерзне, оскільки виклична черга буде чекати, поки відправлений блок не закінчиться, але вона навіть не зможе запуститися (оскільки черга зупинена і чекає)

Коли використовувати sync? Коли вам потрібно дочекатися чогось зробленого в РІЗНІЙ черзі, і лише потім продовжувати роботу над вашою поточною чергою

Приклад використання синхронізації:

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


Чи було б неправильно дзвонити DispatchQueue.main.syncз фонового потоку?
Мед,

@Honey Взагалі ні, в такому дзвінку немає нічого поганого (якщо основна черга не робить нічого важкого та трудомісткого), але на практиці я не можу подумати про ситуацію, коли це вам дійсно потрібно. Безумовно, має бути краще рішення
Андрій Чернуха

1
@Honey Однією з таких ситуацій є оновлення CollectionView PHAssets з API PhotoKit, як показано в документації тут: developer.apple.com/documentation/photokit/…
teacup

1
@teacup цікаво. Мені просто цікаво, як було б інакше, якби ми зателефонували asyncтуди? Я маю на увазі, оскільки згодом у потоці немає нічого іншого, тоді це не має значення. Якби це було DispatchQueue.main.sync {block1}; DispatchQueue.main.sync {block2};тоді, це мало б сенс. Але коли іншого блоку немає, тоді я не можу думати про користь використання DispatchQueue.main.sync {Oneblock}over DispatchQueue.main.async {Oneblock}. Для обох вони отримають пріоритет / безпосередність mainQueue, і ніщо не перешкодить їм.
Мед

3
@Honey "оскільки в потоці більше нічого немає" не відповідає дійсності, коли ви перебуваєте в основному потоці, який відповідає за всі взаємодії користувачів із додатком. Так, наприклад, користувач може видалити іншу фотографію до того, як photoLibraryDidChange повернеться з оновленим джерелом даних, що спричинить фатальну помилку невідповідності.
чашка для

160

Чому паралельність?

Як тільки ви додаєте важкі завдання до свого додатку, наприклад, завантаження даних, це уповільнює роботу вашого інтерфейсу або навіть заморожує його. Паралельність дозволяє виконувати 2 або більше завдань «одночасно». Недоліком цього підходу є та безпека ниток, яку не завжди так легко контролювати. Fe, коли різні завдання хочуть отримати доступ до тих самих ресурсів, як спроба змінити одну і ту ж змінну в різних потоках або доступ до ресурсів, вже заблокованих різними потоками.

Є кілька абстракцій, про які нам слід знати.

  • Черги.
  • Синхронне / асинхронне виконання завдань.
  • Пріоритети.
  • Поширені неприємності.

Черги

Має бути послідовним або одночасним . А також глобальні чи приватні одночасно.

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

Ви можете створювати власні приватні черги (як послідовні, так і одночасні ) або використовувати вже доступні глобальні (системні) черги . Основна чергу є єдиною послідовною чергою з усіх глобальних черг .

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

Якість обслуговування / пріоритет

Черги також мають різне qos (Якість обслуговування), яке встановлює завдання, що виконує пріоритет (тут від найвищого до найнижчого):
.userInteractive - основна черга
.userInitiated - для ініційованих користувачем завдань, на які користувач чекає певної відповіді
.utility - для завдань що займає певний час і не вимагає негайної реакції, наприклад, робота з даними
.background - для завдань, які не пов’язані з візуальною частиною і які не є суворими на час завершення).

Існує також черга

.default, яка не передає інформацію qos . Якщо виявити qos qos було неможливобуде використовуватися між .userInitiated та .utility .

Завдання можуть виконуватися синхронно або асинхронно .

  • Синхронна функція повертає керування поточною чергою лише після завершення завдання. Він блокує чергу і чекає, поки завдання не буде закінчено.

  • Асинхронна функція повертає управління до поточної черги відразу після того, як завдання було надіслано для виконання в іншій черзі. Це не чекає, поки завдання буде закінчено. Це не блокує чергу.

Загальні проблеми.

Найпопулярніші помилки, які допускають програмісти при проектуванні одночасних додатків, такі:

  • Умова гонки - викликається, коли робота програми залежить від порядку виконання частин коду.
  • Інверсія пріоритету - коли завдання з вищим пріоритетом чекають завершення завдань з меншим пріоритетом через блокування деяких ресурсів
  • Тупик - коли кілька черг нескінченно чекають джерел (змінних, даних тощо), які вже заблоковані деякими з цих черг.

НІКОЛИ не викликайте функцію синхронізації в головній черзі .
Якщо ви викличете функцію синхронізації в основній черзі, вона заблокує чергу, а також чекатиме завершення завдання, але завдання ніколи не буде завершено, оскільки воно навіть не зможе запуститись, оскільки вже заблоковано. Це називається глухим кутом .

Коли використовувати синхронізацію? Коли нам потрібно почекати, поки завдання буде закінчено. Fe, коли ми переконуємось, що якусь функцію / метод не викликають подвійно. Якщо ми маємо синхронізацію і намагаємось не допустити її подвійного виклику, поки вона не буде повністю завершена. Ось деякий код для цього:
Як дізнатися, що спричинило звіт про помилку на пристрої IOS?


3
Я не думаю, що "НІКОЛИ не викликати функцію синхронізації в головній черзі" не правильно. Бувають випадки, коли ви викликаєте синхронізацію в основному потоці, наприклад, коли у вас є глобальний лічильник, який потрібно використовувати кожному об’єкту, і збільшити: dispatchQueue.sync {count + = 1; self.orderId = count}
Елісей Штернгольд

6
Клас QOS - .userInteractive НЕ є основною чергою.
Куналь Шах

1
Чи було б неправильно дзвонити DispatchQueue.main.syncз фонового потоку?
Мед

1
@Honey, ні, це не неправильно, якщо зателефонувати до цього, але, з мого досвіду, ви могли б зателефонувати більше DispatchQueue.main.async, крім синхронізації.
Джеймс Кім,

2
Чи не було б точніше сказати, що ви ніколи не повинні викликати функцію sync () у поточній черзі? Неправильно називати sync () в основній черзі, якщо ви перебуваєте в іншій черзі, якщо я правильно розумію.
ykay

0

syncабо asyncметоди не впливають на чергу, в яку вони викликані.

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

Отже, навіть якщо DispatchQueue.main.asyncце асинхронний виклик, додана в ньому важка операція може заморозити інтерфейс, оскільки його операції послідовно виконуються в основному потоці. Якщо цей метод викликається з фонового потоку, керування миттєво повернеться до цього потоку, навіть коли UI здається замороженим. Це тому, що asyncдзвінок здійсненоDispatchQueue.main


0

GCDдозволяє виконати завдання synchronouslyабо asynchronously[Про] [Докладніше]

synchronousФункція (блокувати та очікувати) повертає елемент керування, коли завдання буде виконано

asynchronousФункція (відправлення та продовження) негайно повертає елемент керування, відправляючи завдання для запуску у відповідну чергу, але не чекаючи його завершення.

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