在挂起函数中使用coroutineScope与传递CoroutineScope参数的效果是否一致
两种协程写法的执行效果差异
这两种写法的执行效果完全不同,核心区别体现在以下几个方面:
1. 子协程的等待行为
- 第一种写法用
coroutineScope:它会创建一个全新的子协程作用域,当前挂起函数会挂起等待该作用域内所有launch启动的子协程执行完毕后,才会继续往下走。也就是说,loadContributorsConcurrent函数返回时,内部所有子任务都已经完成。 - 第二种写法用
outerScope.run:只是在传入的外部作用域中执行代码,不会等待内部launch的子协程完成。loadContributorsConcurrent函数会立即返回,而内部的子协程会继续在外部作用域中运行,和当前函数的生命周期完全解绑。
举个实际运行的例子:
// 第一种写法的调用场景 fun main() = runBlocking { loadContributorsConcurrent() println("函数执行完毕") // 所有子协程完成后才会打印这行 } // 第二种写法的调用场景 fun main() = runBlocking { loadContributorsConcurrent(this) println("函数执行完毕") // 函数返回后立刻打印,子协程可能还在后台跑 }
2. 异常处理逻辑
coroutineScope属于结构化并发范畴:如果内部子协程抛出未捕获异常,会立刻取消该作用域下的所有子协程,并且把异常向上传播到调用这个挂起函数的外层协程。外层协程可以选择捕获处理这个异常,或者被取消。outerScope.run的异常由外部作用域处理:子协程的异常会交给传入的outerScope的异常处理器处理。如果外部作用域没有配置异常处理器,可能会直接导致应用崩溃(比如用GlobalScope作为外部作用域时),而且异常只会影响外部作用域的其他协程,不会自动回传给当前挂起函数的调用方。
3. 协程生命周期与泄露风险
- 第一种写法完全符合结构化并发:所有子协程的生命周期被严格限制在
coroutineScope内部,函数执行完毕后,子协程要么完成要么被取消,不会出现协程泄露。 - 第二种写法存在协程泄露风险:子协程属于外部作用域,即使
loadContributorsConcurrent函数执行完毕,只要外部作用域还存活,子协程就会继续运行。比如如果外部作用域是Activity的viewModelScope,当Activity销毁后,ViewModel会被清理,子协程会被取消;但如果传入的是GlobalScope,子协程会一直运行到完成,可能导致内存泄露或无效操作。
内容的提问来源于stack exchange,提问作者ismailbenhallam
相关产品推荐
相关产品推荐

