kotlin - 协程子作业取消

标签 kotlin kotlin-coroutines

根据作业文档,在此作业上调用 [cancel][Job.cancel],但出现异常([CancellationException] 除外)也会取消父作业。 Job.cancel 函数仅接受 CancellationException。我正在测试这种行为,但取消子作业并不会取消父作业,尽管我没有使用 SupervisorJob。

/**
 * Creates a job object in an active state.
 * A failure of any child of this job immediately causes this job to fail, too, and cancels the rest of its children.
 *
 * To handle children failure independently of each other use [SupervisorJob].
 *
 * If [parent] job is specified, then this job becomes a child job of its parent and
 * is cancelled when its parent fails or is cancelled. All this job's children are cancelled in this case, too.

 * --The invocation of [cancel][Job.cancel] with exception (other than [CancellationException]) on this job also cancels parent.--
 *
 * Conceptually, the resulting job works in the same way as the job created by the `launch { body }` invocation
 * (see [launch]), but without any code in the body. It is active until cancelled or completed. Invocation of
 * [CompletableJob.complete] or [CompletableJob.completeExceptionally] corresponds to the successful or
 * failed completion of the body of the coroutine.
 *
 * @param parent an optional parent job.
 */

@Suppress("FunctionName")
public fun Job(parent: Job? = null): CompletableJob = JobImpl(parent)

所以我的测试代码是这样的;

fun main()  {
    val parentJob = Job()
    val scope = CoroutineScope(parentJob)

    suspend fun printText(text: String) {
        println("Before Delay: $text")
        delay(1000)
        println("After Delay: $text")
    }

    val job1 = scope.launch {
        printText("Job#1")
    }

    job1.invokeOnCompletion {
        println("Job#1 completed. Cause = $it")
    }

    val job2 = scope.launch {
        printText("Job#2")
    }

    job2.invokeOnCompletion {
        println("Job#2 completed. Cause = $it")
    }

    println(parentJob.children.joinToString { it.toString() } )

    job1.cancel(CancellationException())

    Thread.sleep(10000)

}

输出如下;

Before Delay: Job#1
Before Delay: Job#2
StandaloneCoroutine{Active}@462d5aee, StandaloneCoroutine{Active}@69b0fd6f
Job#1 completed. Cause = java.util.concurrent.CancellationException
After Delay: Job#2
Job#2 completed. Cause = null

Process finished with exit code 0

我的问题是为什么 job#2 没有被取消?

编辑:

在 @broot 和 @Steyrix 的回答之后。我扩展了测试用例。 现在,如果给定参数是“Job#1”,则 printText(string) 函数会抛出异常。所以我想在android中模拟一个viewmodel。假设我们创建了 CoroutineScope(Job()) 并且我提出了两个不同的请求。其中之一是抛出异常,但它被 try-catch block 捕获。因此其他作业继续执行其作业并且不会被取消。

现在的问题是 SupervisorJob 和 Job 之间有什么区别。为什么 viewmodelscope (CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)) 使用 SupervisorJob ?

扩展示例;

fun main() {
    val parentJob = Job()
    val scope = CoroutineScope(parentJob)

    suspend fun printText(text: String) {
        println("Before Delay: $text")
        if (text == "Job#1") {
            throw IllegalArgumentException("Test")
        }
        delay(1000)
        println("After Delay: $text")
    }

    val job1 = scope.launch {
        try {
            printText("Job#1")
        } catch (e: Exception) {
            println(e)
        }


    }

    job1.invokeOnCompletion {
        println("Job#1 completed. Cause = $it")
    }


    val job2 = scope.launch {
        printText("Job#2")
    }

    job2.invokeOnCompletion {
        println("Job#2 completed. Cause = $it")
    }

    println(parentJob.children.joinToString { it.toString() })


    Thread.sleep(10000)

}

输出;

Before Delay: Job#1
java.lang.IllegalArgumentException: Test
Job#1 completed. Cause = null
Before Delay: Job#2
StandaloneCoroutine{Active}@462d5aee
After Delay: Job#2
Job#2 completed. Cause = null

Process finished with exit code 0

最佳答案

嗯,它的行为符合您引用的文档。您使用了 CancellationException,因此它没有取消父级。

文档中唯一令人困惑的部分是为什么它提到使用 CancellationException 之外的其他异常类型进行取消。这不可能。 cancel() 用于正常取消,而不是失败,因此它永远不会传播到父级。

这看起来像是 Job() 函数文档中的一个小错误。

关于kotlin - 协程子作业取消,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75157904/

相关文章:

java - 咖啡因:当AsyncLoader无法刷新时,请使用过时的值

android - 为 runBlocking 提供 Dispatches.Main 挂起 Android App。为什么?

android - 从挂起函数异常返回对象

android - 未声明可选的字段接受 null 作为值

java - mKeyStore?.getKey ("default_key", null) 在 Kotlin 中获取 null

Android - Kotlin - 对象必须声明为抽象或实现抽象成员

kotlin - 延迟 Kotlin 的 buildSequence 的推荐方法是什么?

kotlin - Kotlin将顺序IO调用包装为Sequence

kotlin - 使用 Kotlin 协程了解作业的所有状态

Kotlin native - 执行可执行文件