1

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

如果父协程被取消,子协程也将停止。如果子协程抛出异常,兄弟协程和父协程会注意到并停止。

除了 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

我再次阅读协程文档以发现对于计算代码,我们必须使用yield或检查协程状态(active, canceled, isCompleted)。然后我进行以下调整:

代码片段 2

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)
}

这次的输出是:

#4
#1

以下是我的问题:

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

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

  3. 在代码片段 2 中,我们真的需要在yield每次想要执行协程代码时使用或检查协程状态吗?因为在我看来,代码会更难阅读。

  4. 我的代码片段或我对协程的理解有问题吗?

注意:对于代码片段 我不想使用,因为在实际项目中,我们反正GlobalScope.runBlocking不会使用。GlobalScope我想创建一个与真实项目一样接近的示例,使用具有某些生命周期的父子范围。

4

1 回答 1

1

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

只有挂起功能是可取消的。替换Thread.sleep(1_000)为暂停延迟,它将被取消。

在代码片段 1 中,为什么即使调用 childJob 也不会执行 #3?

这是因为协程没有被取消。看第一个问题的答案。

在代码片段 2 中,我们真的需要在每次想要执行协程代码时使用 yield 或检查协程状态吗?因为在我看来,代码会更难阅读。

不,你不应该。每个挂起函数都会检查协程的取消。

我的代码片段或我对协程的理解有问题吗?

当您使用协程时,了解挂起函数是必不可少的。Coroutine Basics文档部分很好地解释了它。并且可能是这样的问题会很有用。

于 2019-08-03T04:48:49.197 回答