Что происходит при состояниях гонки, дедлоках, инверсии приоритетов, взрыве потоков, голодании и лайфлоке
Что проверяют
Знаете ли вы термины и, главное, как каждая беда выглядит в iOS: очереди GCD, главный поток, пул потоков Swift Concurrency. Перечисление определений даёт мало, интервьюер ждёт примеров из мобильного приложения и способов проверки.
Как отвечать
Гонка: два потока меняют общее состояние без синхронизации, и результат зависит от планировщика. Лечится последовательной очередью, барьером на конкурентной очереди, замком или актором. Ищется санитайзером потоков в отладочной сборке.
Дедлок: взаимное ожидание. Классика платформы это DispatchQueue.main.sync из главного потока, когда очередь ждёт саму себя. Профилактика: единый порядок захвата ресурсов и запрет на sync в свою же очередь.
Инверсия приоритетов: фоновая задача держит ресурс, нужный высокоприоритетной. GCD частично лечит это донорством приоритета, но ожидание на семафоре донорство ломает, и главный поток стоит.
Взрыв потоков: блокирующие вызовы на глобальной конкурентной очереди заставляют систему подкидывать новые потоки, пока не упрётся лимит пула, а память уходит на стеки и переключения. Пул Swift Concurrency так не делает, потоков там примерно по числу ядер, но блокировать их точно так же нельзя.
Голодание: задача не получает времени, потому что более приоритетные не отпускают процессор. Лайвлок: все живы и работают, но прогресса нет, потому что участники бесконечно уступают друг другу. Помогает случайная задержка перед повтором.
Где спотыкаются
Называют гонкой любой недетерминизм. Состояние гонки это про порядок событий, гонка данных про одновременный неатомарный доступ к памяти, и инструменты у них разные.
Держат ожидание на семафоре в UI-коде, чтобы «дождаться ответа сети». Одним движением получают и заблокированный поток, и инверсию приоритетов, и рост числа потоков. Вместо этого колбэк или await.
Считают, что async/await сам по себе снимает гонки. Он снимает их только там, где состояние спрятано в актор; общий изменяемый объект, к которому лезут из разных задач, остаётся гонкой, а блокирующий вызов внутри задачи по-прежнему съедает поток пула.