異常的傳播
異常傳播是指異常在父子協(xié)程中的傳播蝴悉,什么是父子協(xié)程彰阴,在當前協(xié)程中又啟動一個協(xié)程,那么這個新啟動的協(xié)程就是當前協(xié)程的子協(xié)程拍冠。異常的傳播涉及到協(xié)程作用域的概念
一協(xié)程作用域
1.協(xié)程作用域本身是一個接口
public interface CoroutineScope {
//此作用域的上下文
//上下文由作用域封裝尿这,用于實現協(xié)程生成器,這些生成器是作用域上的擴展
//此屬性除了為了高級用途而訪問[Job]實例外庆杜,不推薦訪問
//通常應該包含一個Job的實例來執(zhí)行結構化并發(fā)
public val coroutineContext: CoroutineContext
}
子類如下
看一個例子射众,來加深理解作用域的父子關系
GlobalScope.launch {
println("GlobalScope ${this}")
launch {
println("A ${this}")
launch {
println("A1 ${this}")
}
}
launch {
println("B ${this}")
}
}.join()
打印
GlobalScope StandaloneCoroutine{Active}@4957b7c3
A StandaloneCoroutine{Active}@507cdd7a
B StandaloneCoroutine{Active}@64a45f82
A1 StandaloneCoroutine{Active}@544353ad
關系如下圖所示
作用域啟動新協(xié)程也是一個新的作用域,它們的關系可以并列晃财,也可以包含叨橱,組成了一個作用域的樹形結構默認情況下,每個協(xié)程都要等待它的子協(xié)程全部完成后拓劝,才能結束自己雏逾。這種形式,就被稱為結構化的并發(fā)
2.我們之前啟動協(xié)程一直用的是GlobalScope郑临,GlobalScope是一個頂級的協(xié)程作用域栖博,此外還有coroutineScope{...}以及 supervisorScope{...}等,這里我們重點講一下跟異常傳播有關系的coroutineScope{...}跟supervisorScope{...}
coroutineScope源碼
//創(chuàng)建協(xié)程作用域并在此范圍調用指定的代碼塊
//該作用域繼承了外部作用域的協(xié)程上下文但是重寫了里面的Job
//這個函數是為分解并行工作而設計的
//當這個作用域內的任何子協(xié)同程序失敗時厢洞,所有其余的子協(xié)程都被取消
public suspend fun <R> coroutineScope(block: suspend CoroutineScope.() -> R): R =
suspendCoroutineUninterceptedOrReturn { uCont ->
val coroutine = ScopeCoroutine(uCont.context, uCont)
coroutine.startUndispatchedOrReturn(coroutine, block)
}
來看一下異常傳播對于coroutineScope{...}中是怎樣的
coroutineScope {
launch {
log(1)
launch {
log(2)
throw ArithmeticException()
}
}
launch {
delay(200)
log(3)
}
}
打印如下
18:19:32:955 [DefaultDispatcher-worker-1] 1
18:19:32:955 [DefaultDispatcher-worker-2] 2
Exception in thread "main" java.lang.ArithmeticException
coroutineScope 當中協(xié)程異常會觸發(fā)父協(xié)程的取消仇让,進而將整個協(xié)程作用域取消掉,如果對 coroutineScope 整體進行捕獲躺翻,也可以捕獲到該異常
supervisorScope源碼
//創(chuàng)建協(xié)程作用域并在此作用域調用指定的代碼塊
//該作用域繼承了外部作用域的協(xié)程上下文但是重寫了里面的Job
//子協(xié)程的失敗不會導致此作用域失敗丧叽,也不會影響其他子協(xié)程
public suspend fun <R> supervisorScope(block: suspend CoroutineScope.() -> R): R =
suspendCoroutineUninterceptedOrReturn { uCont ->
val coroutine = SupervisorCoroutine(uCont.context, uCont)
coroutine.startUndispatchedOrReturn(coroutine, block)
}
來看一下異常傳播在supervisorScope中是怎樣的
supervisorScope{
launch {
log(1)
throw ArithmeticException()
}
launch {
delay(200)
log(2)
}
}
打印如下
18:33:15:486 [DefaultDispatcher-worker-1] 1
18:33:15:523 [DefaultDispatcher-worker-1] 2
Exception in thread "DefaultDispatcher-worker-1" java.lang.ArithmeticException
如何捕獲異常如下
val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable ->
log(throwable)
}
supervisorScope{
launch (exceptionHandler){
log(1)
throw ArithmeticException()
}
launch {
delay(200)
log(2)
}
}