Предел задач в Kotlin
Когда внутри участка есть delay или
долгий расчет, иногда важно не пускать туда
все задачи сразу. Семафор с малым числом
разрешений превращается в шлюз по параллелизму.
При двух разрешениях одновременно работают
не больше двух корутин, остальные стоят в
очереди на acquire, пока кто-то не
закончит участок и не вернет слот:
import kotlinx.coroutines.delay
import kotlinx.coroutines.joinAll
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Semaphore
fun main() {
runBlocking {
val cap = Semaphore(2)
val jobs = List(5) { id ->
launch {
cap.acquire()
try {
println("work $id")
delay(100)
} finally {
cap.release()
}
}
}
joinAll(*jobs.toTypedArray())
}
}
По меткам "work" в консоли видно
волну: сначала пара id, затем следующая пара,
пока все пять не пройдут. Без семафора все
пять строк появились бы сразу.
Тот же шлюз оборачивают в функцию, чтобы
не дублировать try в каждом
запуске. Общий семафор живет снаружи,
а тяжелый шаг лежит в лямбде:
import kotlinx.coroutines.delay
import kotlinx.coroutines.sync.Semaphore
class Loader(private val cap: Semaphore = Semaphore(2)) {
suspend fun runBatch(label: String) {
cap.acquire()
try {
println(label)
delay(80)
} finally {
cap.release()
}
}
}
Шлюз на два одновременных доступа. Шесть
корутин печатают букву этапа, ждут
60 мс и завершаются. Убедитесь
по логу, что активных этапов не больше
двух.
Класс Ripener с семафором на
2 и методом, где пауза
120 мс. Пять фоновых запусков
готовят разные метки "a" …
"e" и ждут общего конца.