Опыт использования параллелизма и синхронизации в Kotlin
Что проверяют
Работали ли вы с конкурентностью руками или знаете её по статьям. Вопрос про опыт, поэтому ждут историю: какая была задача, что применили, как ловили гонку. Следом обычно спрашивают про диспетчеры и про то, чем Mutex отличается от synchronized.
Как отвечать
Начните с важного уточнения: корутины сами по себе не дают параллельности. Последовательные вызовы suspend-функций так и остаются последовательными: параллельность появляется, когда вы явно запускаете async и собираете результаты через awaitAll.
Опорные точки:
- диспетчеры:
Dispatchers.Defaultдля расчётов, число потоков по числу ядер;Dispatchers.IOдля блокирующих вызовов, по умолчанию до 64 потоков, причём оба диспетчера делят общий пул;limitedParallelism(n)ограничивает конкретного клиента, чтобы один медленный сервис не занял весь пул; withContext: переключение контекста внутри suspend-функции, вызывающему коду про потоки знать не нужно;Mutex.withLock: приостанавливает корутину вместо блокировки потока. Он не реентерабельный, поэтому вложенный захват того же мьютекса даёт дедлок;Semaphore: ограничение числа одновременных операций, например не больше пяти загрузок сразу;- без блокировок:
MutableStateFlow.updateс CAS-циклом, атомарные типы, неизменяемые данные и один владелец состояния. Чаще это лучший ответ, чем ручная синхронизация.
Сильный ответ содержит конкретную гонку из вашей практики: параллельные обновления кеша, дублирующийся запрос токена при истёкшей сессии (лечится общим Deferred или мьютексом). Скажите и про отмену: она кооперативная, тяжёлый цикл нужно прерывать через ensureActive() или yield(), иначе корутина продолжит считать после закрытия экрана.
Где спотыкаются
Считают Dispatchers.IO волшебным. Он спасает главный поток от блокировки, но 64 занятых потока это уже очередь, а не параллелизм.
Берут Mutex там, где хватило бы атомарного обновления состояния. Получают лишнюю точку сериализации, а иногда и дедлок из-за вложенного withLock.
Смешивают synchronized с корутинами: внутри такого блока нельзя вызывать suspend-функции, а блокировка потока из пула отбирает его у остальных задач.