Какие бывают проблемы многопоточности и объясни каждую: дедлок, инверсия приоритетов, рейс кондишн, лайфлок
Что проверяют
Можете ли вы различить четыре проблемы по симптомам и показать, как каждая выглядит в iOS: на GCD, блокировках и акторах. Следом обычно дают код с DispatchQueue.main.sync или просят объяснить, почему DispatchSemaphore плохо сочетается с QoS.
Как отвечать
Для каждой проблемы нужны определение, пример и лекарство:
- дедлок: потоки ждут друг друга и стоят навсегда. Классика:
DispatchQueue.main.syncиз главного потока,syncна ту же последовательную очередь, в которой вы уже находитесь, или два лока, взятые двумя потоками в разном порядке. Лечится единым порядком захвата и отказом от вложенныхsync; - инверсия приоритетов: задача с QoS
.userInteractiveждёт лок, который держит задача.background, а ту вытесняют задачи среднего приоритета. Спасает наследование приоритета:os_unfair_lockи мьютексы знают владельца, и система поднимает ему приоритет. УDispatchSemaphoreвладельца нет, поэтому Xcode предупреждает о возможной инверсии; - состояние гонки: результат зависит от порядка выполнения потоков. Частный случай, data race, это одновременный доступ к памяти без синхронизации, где хотя бы одна операция пишет: два потока делают
count += 1или пишут в одинDictionary. Лечится последовательной очередью, локом или актором, а в режиме Swift 6 большую часть гонок данных ловит компилятор через проверкуSendable; - лайфлок: потоки не заблокированы и работают, но прогресса нет. Например, два потока через
try()берут два лока, при неудаче отпускают свой и повторяют, синхронно уступая друг другу. Лечится случайной задержкой перед повтором.
Инструменты: Thread Sanitizer находит гонки данных, Main Thread Checker ловит работу с UI вне главного потока.
Где спотыкаются
Описывают лайфлок как разновидность дедлока. Но при лайфлоке процессор загружен, а при дедлоке потоки спят.
Считают, что акторы решают всё. Актор защищает свои данные от гонок, но из-за reentrancy состояние между двумя await может поменяться, и логическая гонка остаётся.
Блокируют задачу Swift Concurrency семафором, дожидаясь колбэка. Пул потоков кооперативный и ограничен числом ядер, так что зависнуть может всё приложение.