Результат не гарантированно равен 100000: counter++ не атомарен, и параллельные обновления теряются. Для точного счётчика нужна синхронизация.
Разбор coroutine‑кода: задача №2
Результат не гарантированно равен 100000: counter++ не атомарен, и параллельные обновления теряются. Для точного счётчика нужна синхронизация.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
Какой диапазон может принимать число, выводимое в консоль?
suspend fun massiveRun(action: suspend () -> Unit) {
val n = 100 // количество корутин для запуска
val k = 1000 // количество повторений действия каждой корутиной
val time = measureTimeMillis {
coroutineScope { // область для корутин
repeat(n) {
launch {
repeat(k) { action() }
}
}
}
}
println("Выполнено ${n * k} действий за $time мс")
}
var counter = 0
fun main() = runBlocking {
withContext(Dispatchers.Default) {
massiveRun {
counter++
}
}
println("Счетчик = $counter")
}
Ответ
Результат не гарантированно равен 100000: counter++ не атомарен, и параллельные обновления теряются. Для точного счётчика нужна синхронизация.
В коде есть ожидание всех корутин, поэтому проблема не в преждевременной печати, а в гонке чтения и записи. Две корутины могут прочитать одно число и записать одинаковое следующее значение.
В обычной модели чередования операций итог находится в пределах от 1 до 100000; это внешняя граница, а не утверждение о достижимости каждого числа при любом планировщике. Считать 1000 гарантированным минимумом только из числа повторений нельзя: отложенная запись может затереть более свежий результат.
На JVM замените счётчик на AtomicInteger(0) и инкремент на incrementAndGet(): после ожидания всех задач получится ровно 100000. Альтернативы — Mutex вокруг обновления или ограничение доступа одним потоком. Один volatile не делает составной инкремент атомарным.
Источник: kotlinlang.org.