在一些情况下被取消的协程最终没有变成Complte状态?

来源:12-8 案例:仿官方框架实现取消响应2

昨日重现1596341

2020-05-26

取消这块理解上被这个问题卡住了,视频中的dmeo如下,我添加了一些日志,以便分析问题,其执行过程:
18:39:55:454 [DefaultDispatcher-worker-0] 1 //1.launch启动的协程在默认调度器的线程中触发执行log(1)
18:39:55:454 [main] true //2.主线程中继续执行输出JOB当前状态
18:39:55:484 [main @coroutine#0] this coroutine cancel // 3.调用取job.cancel()取消协程
18:39:55:484 [main] false//4.主线程中再次输出job状态false已取消
18:39:55:484 [main] start join//5.主线程join
18:39:56:488 [DefaultDispatcher-worker-1] 2 10086//6.launch的协程执行完hello然后输出结果,然后执行delay,执行delay时会触发cancellationContinuation.getResult()此时在getResult()中调用installCancelHandler(),因为parent已经calcel状态所以会立即执行doCancel()把状态设置成CancelState.Cancelled然后触发getResult()中的
CancelState.Cancelled -> throw CancellationException(“Continuation is cancelled.”)分支抛出异常,所以此时会触发parent即job的resumeWith(exception)把协程最终终止执行,对应的状态也会修改成complete
18:39:56:488 [DefaultDispatcher-worker-1] end join// 上一步中触发结束

即协程结束是因为抛出了异常,这里有个巧合的地方是先cancel然后触发delay直接抛出异常,如果cancel是在delay的等待过程中呢?我在代码job.cancel()前添加Thread.sleep(1500),此时调用delay500毫秒后触发cancel,此时把状态设置成cancel,然后delay的future.cancel(true)取消掉,所以再也不会调用resume换句话说job永远不会触发resumeWith其状态是一直在delay上挂起的。main方法也会一直在join上挂起。delay应该是有bug的应该添加
continuation.invokeOnCancel {
future.cancel(true)
//NEED
continuation.resumeWith(exception)
}

我得理解是:1.协程取消只是把状态设置成了取消而已,协程的代码块还会继续执行,如果执行到suspend方法由于其被取消就会如上所述的会抛出CancellationException,此时协程才真正的停止,如果被取消后,协程代码块中没有挂起点则其会把所有的代码执行然后最终resumeWith结果结束协程,如果协程代码块中存在耗时操作需要判断当前的协程的状态如果取消了则不再需要处理,比如IO读取大量内容时
while(isActive &&xxx) {// 判断协程是active如果不是则减少不必要的操作尽快结束协程代码块。
inputStream.read()
}
----------------------------------------------------代码------------------------------------------
val job = GlobalScope.launch() {
log(1)
val result = hello()
log(2, result)
// try {

        delay(2000)

// } catch (e:CancellationException) {
// e.printStackTrace()
// }
log(3)
}
log(job.isActive)
// Thread.sleep(1500)
job.cancel()
log(job.isActive)
log(“start join”)
job.join()
log(“end join”)
---------------------------------------------------------------------------------------------------

写回答

4回答

bennyhuo

2020-05-26

ps:同学的理解是没问题的。

其实取消这块儿的实现确实比较复杂,我当时学习的时候也发现过官方框架的 bug,也是卡了一周多,心酸~ 

1
1
昨日重现1596341
非常感谢!
2020-05-26
共1条回复

bennyhuo

2020-05-26

CoroutineLite 是开源的,也可以在 Github 给我提 pr 哈。https://github.com/enbandari/CoroutineLite

1
0

bennyhuo

2020-05-26

我看下,可能是有bug,不过fix的方法可能得在 CancellationContinuation 里面。我确认下。

这块儿同学可以把同样的代码用官方框架运行一下,结果应该是符合预期的。

1
0

bennyhuo

2020-05-26

应该是 fix 了,可以看下这笔提交:https://git.imooc.com/coding-398/Kotlin-Tutorials/commit/c0cbeaac8dec05ae342fbe15f77610a578155e7a

0
0

学会Kotlin 突破开发语言瓶颈

如果有一门语言可以取代Java,那么它一定是Kotlin。

1760 学习 · 481 问题

查看课程