На каком потоке выполнится синхронный print из глобальной очереди в iOS
Что проверяют
Понимаете ли вы, что sync это не «выполнить на другом потоке», а «заблокировать текущий поток до конца блока». Вопрос с подвохом: из-за слова «глобальная» кандидат ждёт фонового потока, а печать уходит туда же, откуда её позвали.
Как отвечать
Начните с разделения понятий: очередь это не поток. GCD сам решает, на каком потоке исполнить работу, и у sync есть оптимизация: блок выполняется прямо на вызывающем потоке, потому что будить воркер ради работы, которую всё равно ждут, бессмысленно.
DispatchQueue.global(qos: .utility).sync {
print(Thread.isMainThread) // true, если позвали с главного потока
}Значит ответ такой: на том потоке, с которого сделан вызов. Позвали с главного, печать на главном. Позвали изнутри блока async на глобальной очереди, печать на потоке пула. Контракт GCD обещает только одно: вызывающий поток стоит, пока блок не закончится, а совпадение потоков это деталь реализации, и закладываться на неё в логике нельзя.
Дальше стоит добавить то, ради чего вопрос обычно и задают. Указанный qos на sync не понижает приоритет исполнения: блок идёт с приоритетом вызывающего потока. А DispatchQueue.main.sync с главного потока это мгновенный дедлок, главная очередь ждёт сама себя.
Где спотыкаются
Отвечают «раз очередь глобальная, значит фон». Проверка через Thread.isMainThread или dispatchPrecondition(condition: .onQueue(.main)) показывает обратное, и тяжёлая работа внутри global().sync фризит интерфейс ровно так же, как если бы её написали без очереди.
Второе: оставляют DispatchQueue.main.sync в коде, который иногда вызывается с главного потока, и приложение зависает намертво. Обвязка вида «если уже на главном, выполнить напрямую, иначе sync» проблему не лечит, а прячет: правильнее не иметь синхронных переходов на главную очередь вовсе.
Третье: считают, что у каждой очереди есть свой постоянный поток. Concurrent-очередь раздаёт блоки пулу, serial-очередь гарантирует порядок, но не тождество потока. Поэтому thread-local хранилище и рекурсивные блокировки поверх очередей работают не так, как ожидают, а блокирующие вызовы внутри async на глобальной очереди раздувают пул потоков.