Может ли DispatchQueue выполнять несколько операций синхронно одновременно
Что проверяют
Различаете ли вы две независимые оси: sync и async описывают поведение вызывающего потока, а serial и concurrent описывают саму очередь. Вопрос с подвохом, и следом обычно просят показать, как на этом строится паттерн «читатели и писатель».
Как отвечать
Сначала разведите понятия. sync означает, что вызывающий поток ждёт, пока блок выполнится. Одновременность задач определяется типом очереди: последовательная выполняет блоки по одному, параллельная (attributes: .concurrent или глобальная) допускает несколько сразу.
Отсюда ответ:
- один поток: несколько
syncподряд выполняются строго по очереди, потому что поток ждёт каждый блок; - несколько потоков и параллельная очередь: да, блоки, отправленные через
syncиз разных потоков, работают одновременно, каждый вызывающий ждёт только свой; - последовательная очередь: нет, хоть
sync, хотьasync, блоки идут по одному; concurrentPerform(iterations:): самый прямой ответ на вопрос. Итерации идут параллельно, а вызывающий поток блокируется до завершения всех.
Полезная деталь: GCD в качестве оптимизации часто выполняет блок sync прямо на вызывающем потоке, без переключения.
Практическое применение: кеш с параллельными чтениями через queue.sync { } и записью через queue.async(flags: .barrier) { }. Барьер дожидается окончания текущих чтений и выполняется в одиночку. Работает он только на собственной параллельной очереди. В новом коде ту же задачу часто решает actor из Swift Concurrency.
Где спотыкаются
Отвечают «sync значит последовательно». Это смешение осей, и интервьюер ждёт именно его.
Вызывают sync на ту же последовательную очередь, в которой уже находятся, или DispatchQueue.main.sync с главного потока. Это deadlock, и свежие версии libdispatch часто распознают его и сразу роняют приложение.
Ставят барьер на DispatchQueue.global() и удивляются, что запись идёт параллельно с чтением. На глобальной очереди барьер ведёт себя как обычный async.