Kotlin SupervisorJob неожиданное поведение - PullRequest
2 голосов
/ 17 февраля 2020

У меня есть следующий код:

import kotlin.coroutines.*;
import kotlinx.coroutines.*;

val job = SupervisorJob()
val handler = CoroutineExceptionHandler { _, e ->
    println("Catch: $e")
}
val coroutineContext: CoroutineContext = Dispatchers.IO + job + handler

fun main() {
    val job1 = GlobalScope.launch(coroutineContext) {
        // supervisorScope {
        launch { 
            println("Test0")
            for (i in 0..5) { 
                println("Working! Iteration: $i")
                delay(1000)
            }
            println("Test0 end")
        }


        launch { 
            println("Test1")
            delay(2000)
            throw IllegalAccessException()
        }

        launch { 
            println("Test2")
            delay(3000)
            println("Test2 end")
        }

    }
    //}
    runBlocking { job1.join() }
    println("Test3")
}

Как видите, я использую SupervisorJob, передавая его в coroutineContext в GlobalScope.launch, чтобы избежать отмены любых дочерних элементов в случае ошибки, если это произошло где угодно, и ошибка должна быть обработана в handler. Далее в документации говорится о SupervisorJob:

Creates a supervisor job object in an active state. Children of a supervisor job can fail independently of each other.

Исходя из этого, я ожидаю что-то подобное:

Test0
Working! Iteration: 0
Test1
Test2
Working! Iteration: 1
Catch: java.lang.IllegalAccessException
Working! Iteration: 2
Working! Iteration: 3
Test2 end
Working! Iteration: 4
Working! Iteration: 5
Test0 end
Test3

Но я получаю:

Test2
Test0
Working! Iteration: 0
Test1
Working! Iteration: 1
Catch: java.lang.IllegalAccessException
Test3

Единственное, что помогает, это раскомментировать supervisorScope в приведенном выше коде.

Что я делаю не так? Как я могу установить ожидаемое поведение глобально в ViewModel, например, чтобы избежать переноса каждого launch в supervisorScope?

Заранее спасибо.

Ответы [ 2 ]

1 голос
/ 17 февраля 2020

Как объясняется в этого ответа , вы не можете изменить тип задания для запущенной сопрограммы. Единственное, что находится под вашим контролем - это родительская работа. Поскольку вы сначала запускаете сопрограмму верхнего уровня, а затем, в качестве ее дочерних элементов, запускаете те, чей отказ не хотите влиять на других, лучшим вариантом будет то, что вы уже обнаружили, открывая внутреннюю supervisorScope.

Вы не можете указать глобальную политику того, как данная сопрограмма будет вести себя при неудаче своих детей.

0 голосов
/ 17 февраля 2020

Благодаря @MarkoTopolnik, решение, которое сработало для меня:

val job = SupervisorJob()
val handler = CoroutineExceptionHandler { _, e ->
    println("Catch: $e")
}
val coroutineContext: CoroutineContext = Dispatchers.IO + job + handler
val coroutineScope: CoroutineScope = CoroutineScope(coroutineContext)

fun main() {
    val job1 = coroutineScope.launch {
        println("Test0 start")
        for (i in 0..5) {
            println("Test0 iteration: $i")
            delay(1000)
        }
        println("Test0 end")
    }

    val job2 = coroutineScope.launch {
        println("Test4 start")
        for (i in 0..5) {
            println("Test4 iteration: $i")
            delay(1000)
        }
        println("Test4 end")
    }

    val job3 = coroutineScope.launch {
        println("Test1")
        delay(2000)
        throw IllegalAccessException()
    }

    val job4 = coroutineScope.launch {
        println("Test2 start")
        delay(4000)
        println("Test2 end")
    }

    val jobs = mutableListOf(job1, job2, job3, job4)
    runBlocking { 
        jobs.forEach { it.join() } 
    }

    println("end")
}

Результат:

Test0 start
Test0 iteration: 0
Test1
Test4 start
Test4 iteration: 0
Test2 start
Test0 iteration: 1
Test4 iteration: 1
Test0 iteration: 2
Test4 iteration: 2
Catch: java.lang.IllegalAccessException
Test4 iteration: 3
Test0 iteration: 3
Test2 end
Test4 iteration: 4
Test0 iteration: 4
Test4 iteration: 5
Test0 iteration: 5
Test4 end
Test0 end
end
...