前言:
前几篇文章我们介绍了协程的基础知识、上下文、协程的取消和异常:
这篇文章我们主要是对上篇文章中的结论做一些验证。
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~