取消Kotlin中不可取消的协同路由

取消Kotlin中不可取消的协同路由,kotlin,kotlin-coroutines,Kotlin,Kotlin Coroutines,考虑这个名称所暗示的不可取消的协同程序 fun main(args: Array<String>) = runBlocking { val nonCancellableJob = launch(Dispatchers.Default) { for (i in 1..1000) { if (i % 100 == 0) { println("Non cancellable iteration $i

考虑这个名称所暗示的不可取消的协同程序

fun main(args: Array<String>) = runBlocking {

    val nonCancellableJob = launch(Dispatchers.Default) {
        for (i in 1..1000) {
            if (i % 100 == 0) {
                println("Non cancellable iteration $i")
            }
        }
    }

    println("Cancelling non cancellable job...")
    nonCancellableJob.cancelAndJoin()
}
fun main(args:Array)=运行阻塞{
val nonCancellableJob=launch(Dispatchers.Default){
对于(1..1000中的i){
如果(i%100==0){
println(“不可取消的迭代$i”)
}
}
}
println(“取消不可取消的作业…”)
nonCancellableJob.cancelAndJoin()
}

现在,如果我去掉显式dispatcher
Dispatchers.Default
并使用继承的dispatcher.Default,即
launch{…}
,则协同程序将立即取消,而不打印任何内容。似乎正在取消一个非取消的协同程序!它是一个bug还是什么?

继承的dispatcher运行在您启动它的同一个线程上。这使得
runBlocking
建立了一个顶级循环,该循环遍历在其内部运行的协程,逐个恢复它们。在当前协同进程挂起之前,它无法恢复下一个协同进程


在您的代码中,顶级的
runBlocking
corroutine创建了一个子corroutine,然后在没有挂起的情况下,继续执行其他操作:打印消息并取消刚刚创建的corroutine。在这一点上,协同程序仍然处于已创建但未运行的边缘。您的
cancelAndJoin
调用在此状态下取消它,直到它有机会永远占用
runBlocking
调度程序。它立即将状态更改为“通过取消完成”,整个程序结束。

继承的调度程序在启动它的同一线程上运行。这使得
runBlocking
建立了一个顶级循环,该循环遍历在其内部运行的协程,逐个恢复它们。在当前协同进程挂起之前,它无法恢复下一个协同进程


在您的代码中,顶级的
runBlocking
corroutine创建了一个子corroutine,然后在没有挂起的情况下,继续执行其他操作:打印消息并取消刚刚创建的corroutine。在这一点上,协同程序仍然处于已创建但未运行的边缘。您的
cancelAndJoin
调用在此状态下取消它,直到它有机会永远占用
runBlocking
调度程序。它立即将状态更改为“通过取消完成”,整个程序结束。

当您不使用
调度程序时。默认情况下,
启动
将继承
运行阻塞
的调度程序,这似乎会将执行推迟到需要时

即使您的协同程序在运行时无法取消(因为它没有任何活动检查,也没有挂起点),但仍然可以在开始执行之前取消它

您可以修改launch builder的属性以更改此行为:

要在声明时立即执行它,请执行以下操作:

val nonCancellableJob = launch(start = CoroutineStart.UNDISPATCHED) { ... }
要防止在开始前取消,请执行以下操作:

val nonCancellableJob = launch(start = CoroutineStart.ATOMIC) { ... }

当您不使用
调度程序时。默认值
launch
继承了
runBlocking
的调度程序,它似乎将执行推迟到需要时

即使您的协同程序在运行时无法取消(因为它没有任何活动检查,也没有挂起点),但仍然可以在开始执行之前取消它

您可以修改launch builder的属性以更改此行为:

要在声明时立即执行它,请执行以下操作:

val nonCancellableJob = launch(start = CoroutineStart.UNDISPATCHED) { ... }
要防止在开始前取消,请执行以下操作:

val nonCancellableJob = launch(start = CoroutineStart.ATOMIC) { ... }

谢谢这是否意味着理论上有可能在不修改上下文的
start
元素的情况下启动和执行协同程序,实际上,
runBlocking
的调度程序在到达主
runBlocking
块中的挂起点之前不会执行您的
launch
。是的,我知道了。在
println
前面添加
delay(10)
,强制
runBlocking
调度程序启动并执行协同程序。再次感谢,谢谢。这是否意味着理论上有可能在不修改上下文的
start
元素的情况下启动和执行协同程序,实际上,
runBlocking
的调度程序在到达主
runBlocking
块中的挂起点之前不会执行您的
launch
。是的,我知道了。在
println
前面添加
delay(10)
,强制
runBlocking
调度程序启动并执行协同程序。再次感谢。