• Kotlin - Job 任务/取消


    Job取消的状态

    仅仅终止线程是一个糟糕的方案,协程的取消能够更好的检查状态关闭释放资源。

    • 运行出错或者调用cancel()后协程会在遇到第一个挂起点开始取消该job并抛出CancellationException异常(先处于Cancelling状态:isActive=false,isCancelled=true),之后会调用join()让协程挂起把该job的取消执行完后(再处于Cancelled状态:isCompleted=true)才能继续执行其它,否则会存在其它协程并发执行。推荐使用cancelAndJoin()简化调用。
    • 一旦该job被取消,该job下的子job也会一并取消,但父job兄弟job不受影响,该job不能再用作任何新job父job(不能开启新协程)。

    Job的状态/函数判断isActive( )isCompleted( )isCancelled( )
    New 新创建(optional initial state)falsefalsefalse
    Active 活跃(default initial state)truefalsefalse
    Completing 完成中(transient state)truefalsefalse
    Cancelling 取消中(transient state)falsefalsetrue
    Cancelled 已取消(final state)falsetruetrue
    Compeleted 已完成(final state)falsetruefalse

    取消的异常处理

    协程通过抛出一个 CancellationException异常 来取消 Job。cancel() 可以传参使用不同的异常来指定原因,需要是 CancellationException 的子类才能取消协程。该异常不会导致父协程或其它子协程的取消,可以使用 try-catch-finally 去捕获处理释放资源,推荐使用标准函数 use() 会自动关闭资源。

    1. suspend fun main() = runBlocking {
    2. //没有继承父协程的上下文,有自己的作用域,因此 runBlocking 不会等待 GlobalScope 执行完再结束。
    3. val job = GlobalScope.launch {
    4. try {
    5. //耗时操作
    6. }catch (e:Exception){
    7. //处理异常
    8. }finally{
    9. //释放资源
    10. }
    11. }
    12. delay(1000) //让job运行一下再取消
    13. // job.cancel() //抛异常 JobCancellationException
    14. // job.join() //挂起函数,这样就会等 GlobalScope 取消完再继续执行
    15. job.cancelAndJoin() //简写
    16. }

    无法直接取消的CPU密集型任务 

    CPU密集型任务无法直接被 cancel() 取消,因为直接取消会丢失临时计算数据。可以通过对 Job 状态的判断来响应 cancel() 操作。

    isActive

    加在判断里

    是一个CoroutineScope的扩展属性,判断Job是否处于活跃状态。

    ensureActive()

    写在函数里

    是一个CoroutineScope的扩展函数,返回coroutineContext扩展函数,调用Job的函数,最终调用的是 !isActive,Job处于非活跃状态就抛异常。

    yield()

    不至于抢占太多线程让其它协程拿不到执行权

    会检查所在协程的状态,如果已经取消则报错 CancellationException,此外会尝试让出线程执行权。
    1. suspend fun main() = runBlocking {
    2. val job = launch(Dispatchers.Default) {
    3. var num = 0
    4. while (num < 5 && isActive) { //判断出false便会取消
    5. ensureActive() //检测出false便会取消
    6. yield() //不至于因为任务太抢占资源导致其它协程拿不到线程执行权
    7. println("CPU密集任务")
    8. num++
    9. }
    10. }
    11. delay(1000)
    12. println("等完")
    13. job.cancelAndJoin() //cancel()会将 isActive = false
    14. println("结束")
    15. }

    一定无法取消的任务

    由于我们可以捕获CancellationException异常,在 Job 真正结束前可以做一些事情,由于 Job 响应 cancel() 后已经处于 Cancelling状态,此时启动一个新协程(会被忽略)或者调用挂起函数(会抛异常CancellationException)是无法被执行的。

    • 方式①:指定协程上下文为NonCancellable来得到一个常驻Job不响应 cancel()操作。
    • 方式②:使用invokeOnCompletion()函数,当 Job状态 为 Cancelled 时会执行回调。形参it是一个异常,没有异常值为null,协程被取消值为 CancellationException。
    1. withContext(NonCancellable){ //不会响应取消 }
    2. job.invodeOnCompletion{ //回调代码 }

    超时取消任务 

    超时取消Job,报错不处理会导致程序结束。

    withTimeout()

    public suspend fun withTimeout(timeMillis: Long, block: suspend CoroutineScope.() -> T): T

    超时取消,抛出异常会结束程序。

    withTimeoutOrNull()

    public suspend fun withTimeoutOrNull(timeMillis: Long, block: suspend CoroutineScope.() -> T): T? 

    超时取消并返回null,替代抛出异常。

    1. withTimeout(1000) {
    2. println("")
    3. }
    4. withTimeoutOrNull(1000) {
    5. println("")
    6. } ?: "值为空"

     

  • 相关阅读:
    数据结构课程笔记总结1 - 排序算法
    【华为机试真题 JAVA】勾股数元组-100
    Mac 安装ZooKeeper+kafka基本使用
    python CSSE7030
    gpt不能发送信息了?
    Java中13 种锁的实现方式有哪些?
    python爬虫小案例——汽车之家
    规则推理桌游
    零碎改动(ES6+)
    上海华清远见
  • 原文地址:https://blog.csdn.net/HugMua/article/details/125886126