Child Jobs 不会取消 Kotlin 协程的作用范围

huangapple go评论93阅读模式
英文:

Child Jobs don't cancell scope in Kotlin coroutines

问题

我正在尝试了解有关作业(Jobs)中的取消传播的信息。我认为如果一个作业被取消了,它的父作业也会被取消。

val cancellableScope = CoroutineScope(Dispatchers.Default + Job())
val cancellableScope2 = CoroutineScope(Dispatchers.Default + Job())

runBlocking {

    cancellableScope2.launch {

        cancellableScope.launch {
            throw Error("1")
        }.join()

        cancellableScope.launch {
            println("2")
        }.join()

    }.join()

    cancellableScope2.launch {

        delay(200)
        println("3")

    }.join()

}

我得到了以下输出:

Exception in thread "DefaultDispatcher-worker-2" java.lang.Error: 1
    at MainKt$main$1$1$1.invokeSuspend(Main.kt:21)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:584)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:793)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:697)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:684)
Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@430de60, Dispatchers.Default]

3

我期望只会得到错误信息,但也打印出了 "3"。难道不是当第一个启动失败时,“cancellableScope2” 也会被取消吗?

我尝试改成相同的上下文。

英文:

I'm trying to learn about cancel propagation in Jobs. I thought if a Job get's cancelled, its father too.

val cancellableScope = CoroutineScope(Dispatchers.Default + Job())
val cancellableScope2 = CoroutineScope(Dispatchers.Default + Job())

runBlocking {

    cancellableScope2.launch {

        cancellableScope.launch {
            throw Error("1")
        }.join()

        cancellableScope.launch {
            println("2")
        }.join()

    }.join()

    cancellableScope2.launch {

        delay(200)
        println("llegó 3")

    }.join()

}

I get the next output:

Exception in thread "DefaultDispatcher-worker-2" java.lang.Error: 1
	at MainKt$main$1$1$1.invokeSuspend(Main.kt:21)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
	at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:584)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:793)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:697)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:684)
	Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@430de60, Dispatchers.Default]

3

I expected to only get the error, but "3" get's printed too. Isn't "cancellableScope2" cancelled when first launched fails too?

I tried changing to the same context

答案1

得分: 1

尽管您在cancellableScope2.launch范围中启动,但在此处启动时使用了显式的父范围:

cancellableScope2.launch {

    cancellableScope.launch {
        throw Error("1")
    }.join()

    cancellableScope.launch {
        println("2")
    }.join()

}.join()

这意味着这些作业仅是cancellableScope的子孙作业,与cancellableScope2没有关系,因此它们可以自由地崩溃,而不会影响scope2的状态。

您可能会感到困惑的是launch启动的作业不会返回任何结果。您在它们上面执行join,但这只是等待它们完成。它们成功、失败或被取消都无关紧要。

但是,如果您使用 asyncawait 而不是 launch + join,您将获得您期望的行为。这会创建一个返回结果的 Deferred,因此在这种情况下,await 将重新抛出 Error(1),从而会崩溃 scope2

英文:

Even though you're in cancellableScope2.launch scope you use explicit parent scope when launching here:

cancellableScope2.launch {

    cancellableScope.launch {
        throw Error("1")
    }.join()

    cancellableScope.launch {
        println("2")
    }.join()

}.join()

That means those jobs are descendant of cancellableScope only and have no relation to cancellableScope2, so they are free to collapse without affecting state of scope2.

The thing you might be confused about is that launch-ed jobs don't return any result. You do join on them but it's just a delay until they're complete. Whether they succeed, fail or get cancelled doesn't matter.

However if instead of launch + join you use async and await you'll get the behavior you expect. That creates a Deferred which does return a result so in this case await would rethrow Error(1) which will collapse scope2.

huangapple
  • 本文由 发表于 2023年8月5日 09:19:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/76839788.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定