Различия между DispatchQueue.async и DispatchQueue.sync
Оба метода – async и sync – используются для выполнения кода асинхронно в DispatchQueue, но делают это совершенно по-разному, что влияет на поведение вашего приложения. Понимание этих различий критически важно для написания отзывчивых и корректных многопоточных приложений на Swift (и Objective-C с использованием Grand Central Dispatch).
DispatchQueue.async
DispatchQueue.async планирует блок кода для выполнения асинхронно в указанной очереди. Ключевые характеристики:
- Неблокирующий вызов: Функция
asyncнемедленно возвращает управление вызывающему потоку, даже если задача еще не завершена. - Выполнение в фоновом режиме: Блок кода будет выполнен в один из свободных потоков очереди, когда это возможно. Если очередь пуста, задача будет выполнена текущим потоком (если он доступен).
- Параллелизм: Если очередь настроена для параллельного выполнения, несколько задач могут выполняться одновременно.
- Порядок не гарантируется (по умолчанию): Порядок выполнения задач в очереди не гарантируется, если только очередь не является сериальной (последовательной).
Пример:
DispatchQueue.global().async {
// Длительная операция, например, сетевой запрос или обработка данных
print("Начало выполнения задачи в фоне")
Thread.sleep(forTimeInterval: 2) // Имитация длительной операции
print("Завершение выполнения задачи в фоне")
}
print("Продолжение выполнения основного потока")
В этом примере, async планирует блок кода для выполнения в глобальной очереди. Основной поток не блокируется и сразу же выводит “Продолжение выполнения основного потока”. Блок кода внутри async будет выполнен позже, когда очередь освободится.
DispatchQueue.sync
DispatchQueue.sync планирует блок кода для синхронного выполнения в указанной очереди. Ключевые характеристики:
- Блокирующий вызов: Функция
syncблокирует вызывающий поток до тех пор, пока задача не будет полностью завершена. - Выполнение в текущем потоке (если возможно): Если очередь настроена для последовательного выполнения и текущий поток является потоком очереди, задача выполняется непосредственно в этом потоке. В противном случае, задача будет выполнена в одном из свободных потоков очереди.
- Гарантированный порядок: Задачи выполняются в том порядке, в котором они были добавлены в очередь (для сериальных очередей).
Пример:
DispatchQueue.global().sync {
// Длительная операция
print("Начало выполнения задачи синхронно")
Thread.sleep(forTimeInterval: 2) // Имитация длительной операции
print("Завершение выполнения задачи синхронно")
}
print("Продолжение выполнения основного потока (после завершения задачи)")
В этом примере, sync планирует блок кода для синхронного выполнения в глобальной очереди. Основной поток блокируется до тех пор, пока задача не будет полностью завершена и не выведет “Завершение выполнения задачи синхронно”. Только после этого основной поток продолжит выполнение и выведет “Продолжение выполнения основного потока (после завершения задачи)”.
Сравнение в таблице
| Характеристика | DispatchQueue.async | DispatchQueue.sync |
|---|---|---|
| Блокировка потока | Не блокирует | Блокирует вызывающий поток |
| Выполнение | Асинхронно, в фоновом режиме | Синхронно, в текущем потоке (если возможно) |
| Порядок выполнения | Не гарантируется (по умолчанию) | Гарантируется (для сериальных очередей) |
| Использование | Для выполнения длительных операций без блокировки UI | Для выполнения коротких операций, требующих синхронизации или гарантии порядка |
Важные замечания и потенциальные проблемы
- Deadlock: Использование
syncвнутри блока кода, который уже выполняется в той же очереди, может привести к deadlock. Например, если вы вызываетеsyncиз блока кода, запланированного с помощьюasyncна ту же очередь, текущий поток будет ждать завершения задачи, а задача не сможет завершиться, пока поток не освободится (что невозможно, так как он уже ждет). - UI-блокировка: Никогда не выполняйте длительные операции синхронно в основном потоке (
DispatchQueue.main). Это приведет к зависанию UI и плохому пользовательскому опыту. Используйтеasyncдля выполнения таких операций в фоновом режиме, а затем обновите UI из основного потока после завершения задачи. - Сериальные очереди: Для сериальных очередей
syncгарантирует порядок выполнения задач. Это полезно, когда вам нужно выполнить несколько операций последовательно и избежать гонок данных.
Когда что использовать?
- Используйте
DispatchQueue.asyncдля большинства случаев, особенно для длительных операций, которые не должны блокировать основной поток UI. - Используйте
DispatchQueue.syncтолько тогда, когда вам абсолютно необходимо синхронно выполнить короткую операцию и гарантировать ее завершение перед продолжением выполнения текущего потока. Будьте осторожны с потенциальными deadlock.
В заключение, выбор между async и sync зависит от конкретных требований вашей задачи. Понимание их различий поможет вам писать более эффективный, отзывчивый и надежный код.





