为何Kotlin协程子launch抛出的异常无法被父catch块捕获?
Kotlin协程子launch异常无法被父catch捕获的原因
先看你遇到的问题代码:
val parentScope = CoroutineScope(Dispatchers.Main) parentScope.launch { try{ launch{ // 子协程 // 业务代码.... throw CustomError("error", null) } } catch(cause: CustomError){ // 这里不会执行 } }
这段代码里,子协程抛出的异常会直接导致应用崩溃,父协程的catch块完全捕获不到。但如果把内部的launch换成supervisorScope/coroutineScope/withContext/runBlocking,异常就能被正常捕获:
parentScope.launch { try{ supervisorScope { // 业务代码 throw CustomError("error", null) } } catch(cause: CustomError){ // 使用withContext/supervisorScope时这里会执行 } }
核心原因在于协程的结构化并发规则和异常传播机制的差异:
1. 子launch是独立的异步协程,脱离父协程的执行流
当你在父协程里调用launch创建子协程时,这个子协程是一个独立的Job,它的执行和父协程的代码流是异步分离的:
- 父协程执行到
launch时,只是启动了子协程,然后会继续往下执行,很快就会退出try块 - 子协程的异常是在父协程已经离开
try块之后才抛出的,自然无法被父协程的catch捕获 - 对于未被特殊处理的子协程异常,Kotlin协程的默认行为是将异常交给全局异常处理器处理,最终导致应用崩溃
2. supervisorScope/coroutineScope等是结构化并发的挂起作用域
这些函数属于挂起函数,它们会将内部的协程执行和当前父协程的执行流绑定:
- 调用这些函数时,父协程会挂起等待,直到内部所有协程执行完成
- 内部抛出的异常会直接传播到当前父协程的执行流中,相当于在父协程的
try块内同步抛出了异常,因此能被外部的catch捕获 - 不同作用域的差异仅在于异常对其他子协程的影响(比如
supervisorScope不会因为单个子协程异常取消其他子协程),但都会将异常传播到外部
补充:崩溃异常信息翻译
致命异常: DefaultDispatcher-worker-1 at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106) at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571) at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750) at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678) at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
内容的提问来源于stack exchange,提问作者codeInheaven
相关产品推荐
相关产品推荐

