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 |
| Применение | Защита shared ресурсов | Независимые вычисления |
| 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 очередей для синхронизации shared данных.
При выполнении ресурсоёмких циклических задач на фоновой очереди без 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 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также