Что происходит при дедлоке в главной очереди в iOS
Что проверяют
Понимаете ли вы, что главная очередь последовательная, и что синхронная отправка в неё из неё самой это гарантированное зависание, а не редкая гонка. Заодно смотрят, знаете ли вы, чем такое кончается для приложения.
Как отвечать
DispatchQueue.main.sync блокирует вызывающий поток до конца переданного блока. Если позвать его уже на главном потоке, блок встанет в очередь за текущей работой, а текущая работа ждёт этот блок. Разблокировать друг друга они не могут: это дедлок, мгновенный и воспроизводимый стопроцентно, а не плавающий.
// уже на главном потоке
DispatchQueue.main.sync { print("сюда не доберёмся") }Дальше главный поток стоит: цикл выполнения не крутится, перерисовки нет, касания не обрабатываются, анимации замирают. Пользователь видит застывший экран, а через некоторое время сторожевой таймер системы убивает процесс, и в отчёте о падении появляется характерный код 0x8badf00d, а не привычная трассировка исключения.
Тот же результат даёт любая последовательная очередь, вызывающая sync на самой себе, и пара потоков, синхронно ждущих друг друга навстречу. Что сказать про лечение:
- использовать
async, а если нужен именно главный поток, проверятьThread.isMainThreadпередsync; - в новом коде опираться на
@MainActorиawait: приостановка задачи поток не блокирует; - не мешать семафоры со структурированной конкурентностью,
waitна потоке кооперативного пула приводит к тому же тупику.
Где спотыкаются
Путают дедлок с подвисанием. Долгий цикл или чтение файла на главном потоке тормозят интерфейс, но заканчиваются сами, а sync на себя не закончится никогда, и лечится он совсем иначе.
Второе: объявляют DispatchQueue.main.sync запрещённым всегда. Из фонового потока он законен и иногда нужен. Опасен вызов из самого главного потока и ситуация, когда главный поток параллельно ждёт тот самый фоновый.
Третье: ставят sync только ради того, чтобы вернуть значение из блока. Значение почти всегда можно отдать через замыкание или await, и синхронное ожидание ради удобной сигнатуры это самая частая причина таких зависаний в живом коде.