DispatchQueue — це фундаментальна черга Grand Central Dispatch (GCD) для керування асинхронними завданнями в iOS та macOS. За даними Apple Developer Documentation, 2026, DispatchQueue абстрагує керування потоками від розробника через serial та concurrent черги. GCD автоматично розподіляє завдання по системному пулу потоків, позбавляючи від ручного створення та знищення потоків.
Головне
DispatchQueue — це об'єкт фреймворку Grand Central Dispatch (GCD), який керує виконанням завдань у системних або кастомних чергах потоків. Grand Central Dispatch — низькорівнева бібліотека Apple, доступна з iOS 4 та macOS 10.6, яка повністю абстрагує керування потоками від розробника. GCD використовує пул потоків операційної системи та автоматично масштабує кількість потоків під навантаження пристрою.
Розробнику не потрібно створювати та знищувати потоки вручну — GCD бере це завдання на себе, надаючи простий API через DispatchQueue. Завдання у вигляді замикання (closure) відправляється в чергу через методи sync або async. У першому випадку викликаючий потік блокується до завершення завдання, у другому — виконання продовжується негайно.
За даними Apple (2026), GCD використовує системний пул потоків, який адаптується під кількість ядер та поточне навантаження процесора. Concurrent черга не створює новий потік на кожне завдання — GCD перевикористовує потоки з пулу, що мінімізує накладні витрати на створення потоків.
Grand Central Dispatch складається з трьох ключових компонентів: черги (DispatchQueue), групи (DispatchGroup) та семафори (DispatchSemaphore). Черга — основний елемент, що приймає завдання у вигляді блоків коду. DispatchGroup синхронізує виконання кількох завдань, а DispatchSemaphore обмежує доступ до спільного ресурсу певною кількістю потоків.
Кожна черга GCD пов'язана з певним QoS (Quality of Service) класом, який повідомляє системі про важливість завдання. Система використовує QoS для розподілу процесорного часу між чергами, віддаючи пріоритет більш критичним завданням — наприклад, оновленню UI або обробці дотиків користувача.
Serial черга виконує завдання строго послідовно, одне за одним. Якщо в serial чергу помістити три завдання, друге почнеться тільки після повного завершення першого. Serial черги застосовуються для синхронізації доступу до спільних ресурсів — наприклад, до масиву, який змінюється з кількох частин коду.
Concurrent черга запускає кілька завдань одночасно, розподіляючи їх по доступних потоках із системного пулу. Завдання на concurrent черзі запускаються в порядку надходження (FIFO), але завершуються в довільному порядку, якщо час їх виконання різниться. Concurrent черга не гарантує порядку завершення — тільки порядок запуску.
| Параметр | Serial черга | Concurrent черга |
|---|---|---|
| Порядок виконання | Строго послідовний | Паралельний |
| Кількість потоків | Один | Декілька з пулу GCD |
| Застосування | Захист спільних ресурсів | Незалежні обчислення |
| Main queue | Так (головний потік) | Ні |
| Ризик deadlock | Високий при sync на тій самій черзі | Низький |
Serial черга ідеальна для завдань, що модифікують спільний стан — запис у файл, оновлення моделі даних або робота з Core Data. Використання serial черги гарантує, що дві ділянки коду не змінять одні й ті самі дані одночасно, виключаючи стан гонки без додаткових блокувань.
Concurrent черга підходить для завдань, що не залежать одне від одного: завантаження кількох зображень, паралельні мережеві запити або пакетна обробка даних. GCD автоматично вирішує, скільки завдань запустити одночасно, виходячи з кількості ядер процесора та поточного завантаження системи.
QoS (Quality of Service) — механізм GCD, який повідомляє операційній системі про важливість та терміновість завдання. Система використовує QoS для планування потоків: завдання з вищим QoS отримують більше процесорного часу та запускаються раніше. Значення QoS передається при створенні черги або відправленні конкретного завдання.
У GCD доступно п'ять класів QoS. .userInteractive — найвищий пріоритет для завдань, пов'язаних з UI. .userInitiated — для завдань, ініційованих користувачем. .utility — для фонових завдань з відображенням прогресу. .background — для завдань, невидимих користувачеві. .default — проміжний рівень між userInitiated та utility, використовується за замовчуванням.
За даними Apple (2026), неправильний вибір QoS — одна з частих причин проблем з продуктивністю. Запуск фонового завантаження з QoS .userInteractive відбирає ресурси у UI, викликаючи мікро-лага в анімаціях. Рекомендується вибирати найнижчий QoS, який все ще забезпечує прийнятний час виконання.
При завантаженні зображення для негайного відображення використовуйте .userInitiated — користувач очікує результат. Для попереднього завантаження наступного екрану достатньо .utility. Фонова синхронізація з сервером виконується з .background, мінімізуючи вплив на активні завдання.
DispatchGroup дозволяє відстежувати завершення групи завдань. Коли всі завдання в групі завершені, GCD викликає обробник notify на вказаній черзі. Це особливо корисно при завантаженні кількох незалежних ресурсів — даних профілю, списку друзів та налаштувань, коли інтерфейс потрібно оновити тільки після отримання всіх даних.
DispatchGroup підтримує синхронний виклик wait(), який блокує поточний потік до завершення всіх завдань. Це зручно, коли код не може продовжуватися без результатів групи. Асинхронний варіант — notify() — викликає замикання на вказаній черзі після завершення всіх завдань, не блокуючи викликаючий потік.
DispatchSemaphore контролює доступ до ресурсу, обмежуючи кількість одночасних звернень. Семафор з початковим значенням 3 дозволяє запустити не більше трьох паралельних завдань. При виклику wait() лічильник зменшується, при signal() — збільшується. Якщо лічильник дорівнює нулю, потік блокується до звільнення ресурсу.
Розглянемо три практичних приклади використання DispatchQueue в Swift. Перший демонструє базовий async виклик з поверненням на головний потік, другий — синхронізацію через serial чергу, третій — DispatchGroup для паралельних запитів.
DispatchQueue.main — це serial черга головного потоку, призначена виключно для UI-операцій. Завжди використовуйте її для оновлення інтерфейсу після завершення фонової роботи.
let queue = DispatchQueue.global(qos: .userInitiated)
queue.async {
let data = self.fetchData()
DispatchQueue.main.async {
self.updateUI(with: data)
}
}
Створення власної serial черги з унікальним ідентифікатором синхронізує доступ до змінюваного масиву. Всі операції читання та запису проходять через одну чергу, виключаючи стан гонки.
let serialQueue = DispatchQueue(label: "com.app.items")
var items: [Int] = []
serialQueue.async {
items.append(1)
}
serialQueue.async {
let last = items.last
DispatchQueue.main.async {
print("Last item: \(last)")
}
}
DispatchGroup дозволяє запустити кілька завдань на concurrent черзі та отримати повідомлення про завершення всіх. Це корисно при завантаженні даних для екрану профілю.
let group = DispatchGroup()
let worker = DispatchQueue.global()
worker.async(group: group) { self.loadProfile() }
worker.async(group: group) { self.loadFriends() }
worker.async(group: group) { self.loadSettings() }
group.notify(queue: DispatchQueue.main) {
self.showCompleteUI()
}
Deadlock при sync виклику на serial черзі — найпоширеніша помилка. Якщо завдання на serial черзі викликає queue.sync на тій самій черзі, потік блокується назавжди. Черга чекає завершення поточного завдання, а завдання чекає завершення sync-виклику — класичне взаємне блокування.
Всі операції з UIKit повинні виконуватися на головному потоці. Xcode виявляє такі помилки в Debug через Main Thread Checker. У Release-збірці вони призводять до непередбачуваної поведінки: анімації не запускаються, UI не оновлюється, можливі креші.
Створення сотень custom-черг замість глобальних черг — антипатерн. Кожна черга споживає ресурси системи. Для більшості завдань достатньо глобальних concurrent черг з різним QoS та однієї-двох serial черг для синхронізації спільних даних.
При виконанні ресурсоємних циклічних завдань на фоновій черзі без autoreleasepool пам'ять зростає до завершення всього циклу. ARC звільняє об'єкти тільки при виході з autorelease pool. Загортайте ітерації циклу в autoreleasepool { } для своєчасного звільнення пам'яті.
Часто задавані питання
OperationQueue побудована поверх GCD, але надає більш високорівневий API з залежностями операцій, KVO та підтримкою скасування. DispatchQueue — низькорівнева черга для простих async завдань без керування залежностями.
GCD не підтримує зупинку запущеного завдання. Метод suspend() призупиняє тільки нові завдання, поточне виконується до кінця. Для скасування потрібна ручна перевірка прапорця всередині коду завдання.
Для основного запиту з негайним відображенням результату — .userInitiated. Для попереднього завантаження даних — .utility. Для фонової синхронізації — .background.
GCD не фіксує кількість потоків. Пул потоків динамічно масштабується під навантаження, враховуючи ядра процесора, поточне завантаження та QoS кожного завдання. Максимальна кількість обмежена системою.
UIKit не є потокобезпечним — всі його класи повинні викликатися тільки з головного потоку. Порушення викликає непередбачувану поведінку, пропущені оновлення та креші в Production.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також