如何取消Kotlin协程并确认其已取消?解决竞态条件问题
如何确保协程确实是被自己的取消操作生效,而非已返回结果或被其他方式取消?
在检查Job状态与调用cancel()之间存在竞态条件时,怎么确认是自己执行的取消操作起了作用?
示例代码:
val job = CoroutineScope(Dispatchers.Default).async { delay(1) } if (!job.isCompleted) job.cancel()
核心思路:跳过手动状态检查,直接取消并验证终止原因
首先要明确:Job.cancel()是幂等操作——哪怕协程已经完成,调用它也不会产生副作用。所以没必要先判断isCompleted再取消,直接调用cancel()即可,重点是后续验证协程的终止是否由这次取消触发。
方法1:通过getCompletionExceptionOrNull()判断终止原因
调用cancel()后,等待协程终止(用join()),然后通过该方法获取终止的具体原因:
val job = CoroutineScope(Dispatchers.Default).async { delay(1) } job.cancel() job.join() // 阻塞等待协程终止 val exception = job.getCompletionExceptionOrNull() when (exception) { is CancellationException -> println("协程被成功取消,取消操作生效") null -> println("协程在取消前已正常完成,取消操作未生效") else -> println("协程因其他异常终止:${exception.message}") }
方法2:注册invokeOnCompletion回调异步监听
如果不想阻塞等待join(),可以注册回调异步获取终止信息,还能通过自定义CancellationException标记自己的取消操作,和其他取消行为区分开:
val myCancelMarker = CancellationException("主动触发的自定义取消") val job = CoroutineScope(Dispatchers.Default).async { delay(1) } job.invokeOnCompletion { cause -> when (cause) { myCancelMarker -> println("自己的取消操作生效了") is CancellationException -> println("协程被其他操作取消") null -> println("协程提前完成,取消没赶上") else -> println("协程因异常终止:${cause.message}") } } // 传入自定义原因调用cancel job.cancel(myCancelMarker)
为什么不推荐先检查isCompleted再取消?
你示例中的代码存在明显竞态:在if (!job.isCompleted)判断完成后、调用cancel()前,协程可能已经在其他线程执行完毕,导致cancel()实际上没有生效。直接调用cancel()可以避免这个问题,因为cancel()内部会原子性地处理状态判断,不需要手动做同步。
内容的提问来源于stack exchange,提问作者milan
相关产品推荐
相关产品推荐

