如何正确处理协程的计算代码中的取消?

时间:2019-08-03 02:39:52

标签: kotlin kotlin-coroutines

这是我对协程取消的理解:

  

如果取消了父母的协程,孩子们也将停止。如果一个   子协程抛出异常,同级和父协程   会注意到并停止。

     

除SupervisorJob之外,它会继续处于活动状态,即使   儿童协程停止。

所以,我写了一个代码片段来练习我的理解。

代码段1

fun main() {
    val parentScope = CoroutineScope(SupervisorJob())
    parentScope.launch {
        val childJob = launch {
            try {
                println("#1")
                Thread.sleep(1_000)
                println("#2")
            } catch (e: Exception) {
                println("#3")
            }
        }
        println("#4")
        childJob.cancel()
    }
    Thread.sleep(2_000)
}

这是我的两个期望:

期望1:

#1 is called first because there's no blocking code between child and parent job.
#4 is called because `Thread.sleep` is blocking.
#3 is called because the childJob is cancelled, even though the coroutine is not finished.

期望2:

#4 is called first because the parent coroutine start first.
#1 is called because even though the childJob is cancelled, there's time for #1 to be executed.

但是,代码片段1的实际输出是:

#4
#1
#2

我再次阅读coroutine docs,发现对于计算代码,我们必须使用yield或检查协程状态(activecanceled,{{1 }})。然后,我进行以下调整:

代码段2

isCompleted

这一次的输出是:

fun main() {
    val parentScope = CoroutineScope(SupervisorJob())
    parentScope.launch {
        val childJob = launch {
            try {
                println("#1")
                Thread.sleep(1_000)
                if (isActive) {
                    println("#2")
                }
            } catch (e: Exception) {
                println("#3")
            }
        }
        println("#4")
        childJob.cancel()
    }
    Thread.sleep(2_000)
}

这是我的问题:

  1. 在代码段1中,如何取消取消#4 #1 后的#2?

  2. 在代码段1中,为什么即使调用了childJob却也从不执行#3?

  3. 在代码片段2中,我们每次执行协程代码时是否真的需要使用childJob或检查协程状态?因为我认为代码将更难阅读。

  4. 我的代码段或对协程的理解是否有误?

注意: 我不想将yield用于代码段,因为在实际项目中,我们无论如何都不会使用GlobalScope.runBlocking。我想通过父子作用域和某些生命周期来创建一个与实际项目尽可能接近的示例。

1 个答案:

答案 0 :(得分:1)

  

在代码片段1中,在childJob被执行之后,如何仍然执行#2   取消了吗?

仅暂停功能are cancellable。将localhost:8000替换为暂停的delay,它将被取消。

  

在代码片段1中,为什么即使childJob是,也从不执行#3   叫吗?

这是因为协程未取消。请参阅第一个问题的答案。

  

在代码片段2中,我们是否真的需要使用yield或check   每次我们要执行协程代码时的协程状态?   因为我认为该代码将更难阅读。

不,你不应该。每个暂停功能都会检查协程的取消。

  

我的代码段或我的理解是否有误   协程?

使用协程时,了解挂起功能非常重要。 Coroutine Basics文档部分对此进行了很好的解释。也许SO question会有用。