【发布时间】:2021-07-19 07:17:47
【问题描述】:
Kotlin docs for withContext 说
此函数使用新上下文中的调度程序,如果指定了新的调度程序,则将块的执行转移到不同的线程,并在完成时返回原始调度程序。请注意,withContext 调用的结果以可取消的方式调度到原始上下文中,并带有提示取消保证,这意味着如果调用 withContext 的原始 coroutineContext 在其调度程序开始执行代码时被取消,它丢弃 withContext 的结果并抛出 CancellationException。
当且仅当调度程序被更改时,才会启用上述取消行为。例如,当使用 withContext(NonCancellable) { ... } 时,调度程序没有变化,并且在进入 withContext 内的块或退出时都不会取消此调用。
我尝试设计代码来说明在更改调度程序时取消行为与withContext 未更改时取消行为之间的区别。但我无法复制文档中描述的预期差异。
无论我是否切换调度程序,我都会看到相同的取消行为。
不使用withContext 切换调度程序:
val scope: CoroutineScope = object : CoroutineScope {
override val coroutineContext: CoroutineContext
get() = Executors.newSingleThreadExecutor().asCoroutineDispatcher() + Job()
}
scope.run {
val dispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
val job1 = launch {
println(1)
try {
withContext(NonCancellable) {
println(2)
delay(50)
}
println(3)
withContext(CoroutineName("Foo")) { // <-- Not switching dispatcher
println(4)
withContext(NonCancellable) {
delay(100)
}
println(5)
}
} catch (e: Exception) {
println(e)
}
println(6)
}
val job2 = launch {
println(7)
delay(10)
job1.cancel()
}
println(8)
joinAll(job1, job2)
println(9)
}
输出:
1
8
7
2
3
kotlinx.coroutines.JobCancellationException: StandaloneCoroutine was cancelled; job="coroutine#41":StandaloneCoroutine{Cancelling}@6c57dbdd
6
9
用withContext切换调度器:
val scope: CoroutineScope = object : CoroutineScope {
override val coroutineContext: CoroutineContext
get() = Executors.newSingleThreadExecutor().asCoroutineDispatcher() + Job()
}
scope.run {
val dispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
val job1 = launch {
println(1)
try {
withContext(NonCancellable) {
println(2)
delay(50)
}
println(3)
withContext(dispatcher) { // <--- Switching dispatcher
println(4)
withContext(NonCancellable) {
delay(100)
}
println(5)
}
} catch (e: Exception) {
println(e)
}
println(6)
}
val job2 = launch {
println(7)
delay(10)
job1.cancel()
}
println(8)
joinAll(job1, job2)
println(9)
}
输出:
1
8
7
2
3
kotlinx.coroutines.JobCancellationException: StandaloneCoroutine was cancelled; job="coroutine#41":StandaloneCoroutine{Cancelling}@1f381518
6
9
我认为上面的比较表明,withContext 在进入withContext 块时会抛出 CancellationException,无论调度器是否切换。但这与文档相矛盾。
我错过了什么?我误解了文档吗?
【问题讨论】:
-
“我试图设计代码来说明调度程序更改与未更改时的取消行为之间的区别” - 请向我们展示您到目前为止所拥有的内容。
-
@Dai 会的。很乱,所以我没有把它包括在内。但我会清理它并添加到帖子中。
-
你错过了这部分
It immediately checks for cancellation of the resulting context and throws CancellationException if it is not active。在这种情况下,withContext也不会将JobCancellationException与NonCancellable一起抛出,因为NonCancellable始终处于活动状态