Котлин параллельные сопрограммы
Допустимо ли сохранять несколько экземпляров заданий из отдельных сопрограмм. Допустим, я хочу запустить пару сопрограмм одновременно, в которых они не связаны и не могут происходить в одной сопрограмме, но я хочу, чтобы они работали параллельно. В Android я должен сохранить экземпляр задания, чтобы отменить задание в методе onDestroy. Было бы приемлемо сохранить каждую работу отдельно в списке, или я нарушаю какое-то правило. Я знаю, что в RX есть подписки, почему в Kotlin Coroutines нет эквивалента?
val jobList = arrayListOf<Job>()
fun startJob1() {
jobList.add(launch {
//do some work
})
fun startJob1() {
jobList.add(launch {
//do some other unrelated work
})
override fun onDestroy() {
super.onDestroy()
cancelAllActiveJobs(jobList)
}
Имеет ли этот тип архитектуры смысл для сопрограмм?
2 ответа
Вы можете вручную хранить список Job
объекты, которые вы запускаете, но вы также можете использовать иерархию заданий "родитель-потомок", которая доступна "из коробки" для упрощения управления и сохранения списка запущенных заданий.
Итак, во-первых, вместо списка заданий вы определяете ссылку на родительское задание:
val job = Job()
Затем, каждый раз, когда вы запускаете новую сопрограмму, вы делаете ее ребенком этой работы:
fun startJob1() {
launch(job) { // make it a child
//do some work
}
}
fun startJob1() {
launch(job) { // make it a child
//do some other unrelated work
}
}
Наконец, когда вам нужно уничтожить ваш объект и отменить все задания, вы просто отменяете родительское задание.
override fun onDestroy() {
super.onDestroy()
job.cancel()
}
Преимущество этого подхода в том, что список заданий управляется автоматически. Новые сопрограммы могут быть запущены и добавлены в родительское задание, а по завершении они автоматически удаляются из родительского задания.
Вы можете прочитать больше в соответствующем разделе руководства: https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md#cancellation-via-explicit-job
Это вполне выполнимо, и ничего особенного. Посмотрите на этот простой пример, который создает 100 тыс. Заданий одновременно:
val jobs = List(100_000) { // launch a lot of coroutines and list their jobs
launch {
delay(1000L)
print(".")
}
}
jobs.forEach { it.join() }
Чтобы сделать отменяемую работу, она должна сама проверить, была ли она отменена извне, что вы можете сделать с помощью цикла над состоянием активности: while (isActive)
,
Вот пример с двумя заданиями, которые впоследствии отменяются:
fun main(args: Array<String>) = runBlocking {
val startTime = System.currentTimeMillis()
val jobs = arrayListOf<Job>()
jobs += launch {
var nextPrintTime = startTime
var i = 0
while (isActive) { // check if still active
if (System.currentTimeMillis() >= nextPrintTime) {
println("Job1: Sleeping ${i++} ...")
nextPrintTime += 500L
}
}
}
//another job
jobs += launch {
while (isActive) { // check if still active
if (System.currentTimeMillis() >= 42) {
println("Job2: Sleeping 42 ...")
delay(500L)
}
}
}
delay(1300L) // delay a bit
println("main: Cancelling the sleeping job!")
jobs.forEach { it.cancelAndJoin() } // cancels the job and waits for its completion
}