Kotlin之协程取消和异常的运用

前言:

前几篇文章我们介绍了协程的基础知识、上下文、协程的取消和异常:
第一节:kotlin之协程基础知识
第二节:Kotlin之协程的上下文
第三节:Kotlin之源码解析协程的取消和异常流程

这篇文章我们主要是对上篇文章中的结论做一些验证。

1.子Job调用cancel()方法,不会取消父Job

fun main() {
    val scope = CoroutineScope(Dispatchers.Default)
    println("root Job: ${scope.coroutineContext[Job]}")
    val job = scope.launch {
        println("child Job: ${coroutineContext.job}\n")
    }
    job.cancel()
    runBlocking { delay(100) }

    println("root Job: ${scope.coroutineContext[Job]?.isActive}")
    println("child Job: ${job.isActive}")
}
// 输出
root Job: JobImpl{Active}@dc24521
child Job: StandaloneCoroutine{Cancelling}@347ba574

root Job: true
child Job: false

调用scope.launch()方法创建的Job是我们在CoroutineScope()方法中创建Job的子Job,当我们调用job.cancel()时,launch()函数中的Job已经取消了,但是它的父Job仍然处于活跃状态。Job的取消只是更改了Job的状态,并不能影响协程的执行流程。我们需要在适当的地方添加isActive字段来协作协程的取消。

2.父Job调用cancel()方法,会循环遍历取消子Job,并等待子Job执行完成后,再继续执行自己的取消流程

fun main() {
    val scope = CoroutineScope(Dispatchers.Default)
    println("parent Job: ${scope.coroutineContext[Job]}")
    val child1 = scope.launch {
        println("child1 Job: ${coroutineContext[Job]}")
    }
    val child2 = scope.launch {
        println("child2 Job: ${coroutineContext[Job]}")
    }
    scope.coroutineContext[Job]?.cancel()

    println("parent Job: ${scope.coroutineContext[Job]?.isActive}")
    println("child1 Job: ${child1.isActive}")
    println("child2 Job: ${child2.isActive}")
    runBlocking { delay(100) }
}
// 输出
parent Job: JobImpl{Active}@dc24521
child1 Job: StandaloneCoroutine{Active}@6ecd1d5d
child2 Job: StandaloneCoroutine{Active}@7524996b

parent Job: false
child1 Job: false
child2 Job: false

3.如果一个子Job抛异常了,那么默认情况下它会取消它的父Job,并且会通过childCancelled() 方法逐级向上传递,直到根Job

fun main() {
    val scope = CoroutineScope(Dispatchers.Default + CoroutineExceptionHandler{ _,e ->
        println("${e.message}")
    })

    var childJob:Job? = null
    val parentJob = scope.launch {
        childJob = launch { throw Exception("throw exception.\n") }
    }

    runBlocking { delay(100) }
    println("rootJob: ${scope.coroutineContext[Job]?.isActive}")
    println("parentJob: ${parentJob.isActive}")
    println("childJob: ${childJob?.isActive}")
}
// 输出
throw exception.

rootJob: false
parentJob: false
childJob: false

4.如果我们使用函数SupervisorJob()、supervisorScope()来创建一个Job,如果它的子Job发生异常则不会影响到的它的父Job,原因是重写了childCancelled()的方法

使用SupervisorJob():

fun main() {
    val scope = CoroutineScope(SupervisorJob() + CoroutineExceptionHandler{ _, e ->
        println("${e.message}\n")
    })
    println("parent Job: ${scope.coroutineContext[Job]}")
    val childJob = scope.launch {
        throw Exception("child job throw exception")
    }

    runBlocking { delay(100) }
    println("parent Job: ${scope.coroutineContext[Job]?.isActive}")
    println("child Job: ${childJob.isActive}")
}
// 输出
parent Job: SupervisorJobImpl{Active}@5b275dab
child job throw exception

parent Job: true
child Job: false

使用作用域挂起函数supervisorScope():

fun main() {
    val scope = CoroutineScope(CoroutineExceptionHandler{ _, e ->
        println("${e.message}")
    })
    println("root Job: ${scope.coroutineContext[Job]}")

    var childJob:Job? = null
    val parentJob = scope.launch {
        println("parent Job: ${coroutineContext[Job]}")
        supervisorScope {
            launch {
                childJob = coroutineContext[Job]
                println("child Job: $childJob \n")
                throw Exception("child job throw exception\n")
            }
        }
    }
    runBlocking { delay(50) }
    println("root Job: ${scope.coroutineContext[Job]?.isActive}")
    println("parent Job: ${parentJob.isActive}")
    println("child Job: ${childJob?.isActive}")
}
// 输出
root Job: JobImpl{Active}@5b275dab
parent Job: StandaloneCoroutine{Active}@4e7cb0ad
child Job: StandaloneCoroutine{Active}@7d2af9b5 

child job throw exception

root Job: true
parent Job: false
child Job: false

5. 如果一个父Job的多个子Job都发生了异常,那么我们总是取第一个异常来作为父Job异常的原因

fun main() {
    val scope = CoroutineScope(SupervisorJob() + CoroutineExceptionHandler{ _, e ->
        println("${e.message}\n")
    })
    println("parent Job: ${scope.coroutineContext[Job]}")
    scope.launch {
        launch {
            throw Exception("child job1 throw exception")
        }

        launch {
            throw Exception("child job2 throw exception")
        }
    }
    runBlocking { delay(100) }
}
// 输出
parent Job: SupervisorJobImpl{Active}@5b275dab
child job1 throw exception

6.父Job可以调用cancelChildren()方法来取消它所有的子Job

fun main() {
    val scope = CoroutineScope(SupervisorJob() + CoroutineExceptionHandler { _, e ->
        println("${e.message}\n")
    })
    var childJob1:Job? = null
    var childJob2:Job? = null
    val parentJob = scope.launch {
        childJob1 = launch {
            println("child1 launch.")
            delay(100)
            println("child1 execute")
        }

        childJob2 = launch {
            println("child2 launch.")
            delay(100)
            println("child2 execute")
        }
    }
    runBlocking { delay(50) }
    parentJob.cancelChildren()
    println("childJob1: ${childJob1?.isActive}")
    println("childJob2: ${childJob2?.isActive}")

}
// 输出
child1 launch.
child2 launch.
childJob1: false
childJob2: false

总结:
这篇文章我们只是对上一节的结论做了验证。主要还是需要我们要掌握协程的取消和异常的执行流程,下篇文章将继续介绍协程中的另外一个知识Channel~

  • 19
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值