You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CoroutineScope配SupervisorJob与supervisorScope的行为差异疑问

Kotlin协程SupervisorJob未按预期工作问题

我在学习Kotlin协程时,了解到SupervisorJob()可以避免子协程取消牵连其他子协程——coroutineScope里某个子协程失败会取消所有子协程,但supervisorScope只会取消失败的那个。不过我尝试给CoroutineScope添加SupervisorJob作为上下文时,没得到预期结果:本来想让getData1()和getData3()的结果正常打印,结果getData2()抛出异常后所有协程都被取消了。

相关代码如下:

fun main() = runBlocking {

    val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable ->
        println(throwable)
    }

    val customScope = CoroutineScope(SupervisorJob() + exceptionHandler)


    customScope.launch {
        launch {
            getData1().also { println(it) }
        }
        launch {
            getData2().also { println(it) }
        }
        launch {
            getData3().also { println(it) }
        }
     }.join()

}


private suspend fun getData1(): String? {
    delay(1000)
    return "data 1"
}

private suspend fun getData2(): String? {
    delay(300)
    throw RuntimeException("While getting Data 2 exception happened")
}

private suspend fun getData3(): String? {
    delay(800)
    return "data 3"
}

问题原因

你创建的customScope虽然用了SupervisorJob(),但在customScope.launch内部启动的三个子协程,它们的父Job是这个外层launch生成的普通Job,而非customScope的SupervisorJob()。SupervisorJob的隔离作用仅对其直接子协程生效,内层launch会继承外层launch的Job上下文,所以当getData2()抛出异常时,外层launch的Job会触发所有子协程取消,导致getData1()和getData3()也被终止。

解决方案

有两种方式可以实现预期的隔离效果:

方式一:用supervisorScope包裹内层协程

将三个内层launch放到supervisorScope中,让它们直接处于supervisorScope的作用域下,单个子协程失败不会影响其他协程:

fun main() = runBlocking {

    val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable ->
        println(throwable)
    }

    val customScope = CoroutineScope(SupervisorJob() + exceptionHandler)

    customScope.launch {
        supervisorScope {
            launch {
                getData1().also { println(it) }
            }
            launch {
                getData2().also { println(it) }
            }
            launch {
                getData3().also { println(it) }
            }
        }
     }.join()
}

private suspend fun getData1(): String? {
    delay(1000)
    return "data 1"
}

private suspend fun getData2(): String? {
    delay(300)
    throw RuntimeException("While getting Data 2 exception happened")
}

private suspend fun getData3(): String? {
    delay(800)
    return "data 3"
}

方式二:让内层协程直接继承SupervisorJob上下文

修改内层launch的上下文,让它们直接继承customScope的SupervisorJob,而非外层launch的普通Job:

fun main() = runBlocking {

    val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable ->
        println(throwable)
    }

    val customScope = CoroutineScope(SupervisorJob() + exceptionHandler)

    customScope.launch {
        // 直接继承customScope的上下文,包含SupervisorJob
        launch(coroutineContext) {
            getData1().also { println(it) }
        }
        launch(coroutineContext) {
            getData2().also { println(it) }
        }
        launch(coroutineContext) {
            getData3().also { println(it) }
        }
     }.join()
}

private suspend fun getData1(): String? {
    delay(1000)
    return "data 1"
}

private suspend fun getData2(): String? {
    delay(300)
    throw RuntimeException("While getting Data 2 exception happened")
}

private suspend fun getData3(): String? {
    delay(800)
    return "data 3"
}

验证结果

修改后运行代码,会输出如下内容,符合预期:

java.lang.RuntimeException: While getting Data 2 exception happened
data 3
data 1

内容的提问来源于stack exchange,提问作者nasibeyyubov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 10:05:38