这是我对协程取消的理解:
如果父协程被取消,子协程也将停止。如果子协程抛出异常,兄弟协程和父协程会注意到并停止。
除了 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
以下是我的问题:
childJob在代码片段 1 中,#2 在取消后如何仍然执行?在代码片段 1 中,为什么 #3 即使
childJob被调用也不会执行?在代码片段 2 中,我们真的需要在
yield每次想要执行协程代码时使用或检查协程状态吗?因为在我看来,代码会更难阅读。我的代码片段或我对协程的理解有问题吗?
注意:对于代码片段
我不想使用,因为在实际项目中,我们反正GlobalScope.runBlocking不会使用。GlobalScope我想创建一个与真实项目一样接近的示例,使用具有某些生命周期的父子范围。