为何使用SupervisorJob后协程作用域仍会被取消?
为什么用SupervisorJob创建的CoroutineScope仍会因子协程异常取消整个作用域?
你的问题核心在于SupervisorJob的作用层级没有覆盖到真正需要隔离的子协程,具体原因和解决办法如下:
问题根源
你代码里的CoroutineScope(SupervisorJob() + exceptionHandler)确实创建了带SupervisorJob的作用域,但后续调用coroutineScope.launch时,这个launch会生成一个普通Job作为外层SupervisorJob的子Job。而你在这个普通Job内部又启动了两个子协程——这两个协程的直接父是这个普通Job,而非外层的SupervisorJob。
在协程的异常传播规则里:
- 普通Job下的子协程抛出未捕获异常时,会直接取消父Job,同时父Job会取消所有其他子Job,所以第二个协程也被终止,
Coroutine 2 is done...无法打印。 - SupervisorJob的隔离特性只对它的直接子协程生效,不会传递给嵌套的普通Job的子协程。
而supervisorScope之所以正常,是因为它本身就是一个协程构建器,会自动创建一个局部的SupervisorJob作为当前协程的子Job,内部的所有子协程都是这个SupervisorJob的直接子,因此单个子协程的异常不会影响其他子协程。
修正方案
方案1:让两个子协程直接成为外层SupervisorJob的子协程
把两个launch直接挂在外层的CoroutineScope下,跳过中间的普通Job:
fun main() = runBlocking { val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable -> println("Caught $throwable") } val coroutineScope = CoroutineScope(SupervisorJob() + exceptionHandler) // 直接在外层Scope下启动协程,父Job是SupervisorJob coroutineScope.launch { println("Coroutine 1 is working...") delay(300L) throw Exception("Exception in coroutine 1") } coroutineScope.launch { println("Coroutine 2 is working...") delay(500L) println("Coroutine 2 is done...") // 现在会正常打印 } // 等待外层Scope的所有协程完成 coroutineScope.coroutineContext[Job]!!.join() }
方案2:在内部嵌套中使用supervisorScope
如果必须保留内部的嵌套结构,用supervisorScope包裹两个子协程,为它们创建局部的SupervisorJob:
fun main() = runBlocking { val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable -> println("Caught $throwable") } val coroutineScope = CoroutineScope(SupervisorJob() + exceptionHandler) val job1 = coroutineScope.launch { supervisorScope { // 局部SupervisorJob隔离内部协程 launch { println("Coroutine 1 is working...") delay(300L) throw Exception("Exception in coroutine 1") } launch { println("Coroutine 2 is working...") delay(500L) println("Coroutine 2 is done...") // 正常打印 } } } job1.join() }
总结
- SupervisorJob的异常隔离只作用于直接子协程,嵌套协程默认继承父协程的普通Job,不享受隔离。
supervisorScope是更便捷的局部隔离方案,无需手动管理Job层级。
内容的提问来源于stack exchange,提问作者Hayk Mkrtchyan
相关产品推荐
相关产品推荐

