В чем разница синхронного и асинхронного выполнения в GCD
Что проверяют
Понимаете ли вы, что sync блокирует вызывающий поток, и знаете ли главную причину зависаний.
Как отвечать
Оба метода ставят задачу в очередь. Разница в том, что происходит с вызывающим кодом:
asyncвозвращает управление сразу, не дожидаясь выполнения;syncблокирует вызывающий поток, пока задача не выполнится.
Важная тонкость: sync не означает «выполнить на текущем потоке». Задача принадлежит очереди, просто GCD в качестве оптимизации часто выполняет её на вызывающем потоке, чтобы не переключаться.
Главная опасность, ради которой вопрос и задают, это взаимная блокировка:
DispatchQueue.main.sync { ... } // из главного потока: вечное зависаниеГлавная очередь последовательная. Вызывая sync на неё из неё же, вы просите очередь выполнить задачу, которая станет в конец, но освободиться очередь не может, потому что ждёт эту задачу. То же самое с любой своей последовательной очередью: sync на текущую очередь изнутри неё это зависание намертво.
Другие издержки sync:
- занятый поток: заблокированный поток ничего не делает, но существует. Много таких блокировок ведут к разрастанию пула потоков и падению производительности;
- инверсия приоритетов: высокоприоритетный поток ждёт низкоприоритетную задачу.
Практическое правило: по умолчанию async. sync уместен в двух случаях: короткая защита общего состояния (чтение из последовательной очереди или чтение через барьер в конкурентной) и ситуация, когда результат нужен немедленно и вы точно не на этой же очереди.
Стоит добавить, что в современном коде эта задача решается через async/await и акторы: там ожидание приостанавливает задачу, а не блокирует поток, поэтому классов ошибок с зависанием становится меньше.
Где спотыкаются
Пишут DispatchQueue.main.sync и получают зависание.
Используют sync для ожидания сетевого ответа.
Считают, что sync выполняет код на текущем потоке по определению.
Оборачивают DispatchSemaphore вокруг асинхронного вызова и блокируют главный поток.