从 CoroutineScope 启动的 Job 和 SupervisorJob 也是一个 SupervisorJob 吗?

Is Job launched from CoroutineScope with SupervisorJob also a SupervisorJob?

在下面的例子中。 childJob 是 SupervisorJob 吗?

val viewModelScope: CoroutineScope = CoroutineScope(Dispatchers.Main + SupervisorJob())
val childJob = viewModelScope.launch {/* do something */}

或类似的东西是必要的:

val viewModelScope: CoroutineScope = CoroutineScope(Dispatchers.Main + SupervisorJob())
val childJob = viewModelScope.launch(SupervisorJob()) {/* do something */}

据我了解 SupervisorJob 它不会影响 children 也将其修改为表现得像 SupervisorJob,也就是说 children SupervisorJob 不会失败parent 作业,但是如果 children 的 child 作业失败,那么那些 children 也会失败。该行为不会通过作业的 children 传递。

在我看来,这也是有道理的,因为您的取消等默认行为应该是相同的,直到您为一个特定的协程更改它。

要回答您的问题,您需要明确声明您想要启动具有 SupervisorJob 行为的协程。

我会让代码来说话:

fun main() = runBlocking {
    val scopeSupervisor = SupervisorJob()
    val explicitSupervisor = SupervisorJob()
    val explicitPlainJob = Job()
    val scope = CoroutineScope(Dispatchers.Main + scopeSupervisor)

    scopeSupervisor.printJobClass("scopeSupervisor")
    explicitPlainJob.printJobClass("explicitPlainJob")
    println()
    scope.launch {
        coroutineContext.printJobClass("context 1")
        scopeSupervisor.printChildren("scopeSupervisor")
    }.join()
    scope.launch(explicitSupervisor) {
        coroutineContext.printJobClass("context 2")
        explicitSupervisor.printChildren("explicitSupervisor")
    }.join()
    scope.launch(explicitPlainJob) {
        coroutineContext.printJobClass("context 3")
        explicitPlainJob.printChildren("explicitPlainJob")
    }.join()
}

fun CoroutineContext.printJobClass(msg: String) {
    println("$msg class: ${this[Job]!!.className}")
}

fun Job.printChildren(msg: String) {
    println("$msg children: ${children.map { it.className }.joinToString()}")
}

val Any.className get() = this::class.java.simpleName

这会打印

scopeSupervisor class: SupervisorJobImpl
explicitPlainJob class: JobImpl

context 1 class: StandaloneCoroutine
scopeSupervisor children: StandaloneCoroutine
context 2 class: StandaloneCoroutine
explicitSupervisor children: StandaloneCoroutine
context 3 class: StandaloneCoroutine
explicitPlainJob children: StandaloneCoroutine

解读:

范围内的作业和显式传入的作业都没有成为与协程关联的作业。协程作业始终是 StandaloneCoroutine 类型,它是传入作业的子作业。

另请注意,传入一个不是作用域作业子项的显式 SupervisorJob 是错误的。如果您取消范围内的顶级作业,这将不会传播到您创建的显式 SupervisorJob