Может ли возникнуть дедлок при использовании последовательной очереди и вызове sync внутри нее в iOS?
Что проверяют
Понимаете ли вы, что делает sync на самом деле: блокирует текущий поток до завершения задачи. Интервьюеру важно, чтобы вы сами построили цепочку ожиданий и объяснили, почему она замыкается. Следом обычно спрашивают про DispatchQueue.main.sync с главного потока и про то, как этого избежать в Swift Concurrency.
Как отвечать
Да, и это классический дедлок. Последовательная очередь выполняет задачи строго по одной. Если код уже работает внутри задачи очереди queue и вызывает queue.sync { ... }, новая задача встаёт в очередь за текущей. Текущая ждёт, пока выполнится новая, а новая ждёт, пока освободится очередь.
Варианты, которые стоит назвать:
- прямой случай:
DispatchQueue.main.syncиз главного потока. libdispatch распознаёт повторный вход в очередь, которой уже владеет текущий поток, и роняет приложение с сообщениемdispatch_sync called on queue already owned by current thread. Поэтому на практике это чаще краш, чем зависание; - косвенный случай: очередь A синхронно ждёт очередь B, а задача на B синхронно ждёт A. Такой цикл libdispatch не распознаёт, и приложение просто висит;
- реальный код: потокобезопасная обёртка, где геттер читает через
queue.sync, а внутри блока на той же очереди кто-то обращается к этому геттеру.
Как избегать. Использовать async, если результат не нужен немедленно. Перед queue.sync в обёртках ставить dispatchPrecondition(condition: .notOnQueue(queue)), чтобы ошибка ловилась в отладке. Если вызов может прийти с любой очереди, проверять текущую через DispatchSpecificKey и выполнять блок сразу. В новом коде вместо очереди с sync берут actor: обращения к нему идут через await и поток не блокируют.
Где спотыкаются
Говорят, что дедлок бывает только с двумя потоками. Здесь поток один, и он ждёт сам себя.
Считают, что sync на параллельную очередь изнутри неё тоже всегда вешает приложение. Вложенный вызов выполнится, если это не барьерный блок: проблема именно в последовательном исполнении.
Лечат дедлок заменой sync на async везде подряд. Код, который ждал результат, начинает читать ещё не записанные данные, и дедлок превращается в гонку.