Kotlin 协程中 runBlocking 的协程作用域是什么?

Gis*_*453 2 kotlin kotlin-coroutines

什么是协程作用域

runBlocking {
 // some code here
}
Run Code Online (Sandbox Code Playgroud)

在 Kotlin 协程中?

它对于调用它的类来说是本地的,并且当类被垃圾收集时,协程也会被垃圾收集吗?或者如果它仍在运行,会导致内存泄漏吗?

Jof*_*rey 6

与launchasync不同,runBlocking它是一个非常特殊的协程构建器,因为它应该在顶层使用。因此它不在作用域内CoroutineScope运行(如您所见,没有接收器)。它更应该成为结构化并发的“根”。

runBlocking实际上为您提供了一个范围,因此您可以在其中启动“子”协程。真正重要的是,runBlocking将等待您在该范围内启动的所有协程(子协程)完成后再返回。

取消 runBlocking?

如果协程挂起,您无法真正从外部取消runBlocking协程本身(就像您通过取消launchor的范围所做的async那样),因为它不是异步任务 - 它正在阻塞线程。您可以中断运行它的线程,或者也可以跟踪嵌套协程并显式取消它们runBlocking完成。

runBlocking仅当所有子协程完成(或取消)后,调用才会返回。(从内部)抛出异常也会取消所有子协程并重新抛出runBlocking该异常。所以这也是一种runBlocking从内部“取消”的方式。

它对于调用它的类来说是本地的,并且当类被垃圾收集时,协程也会被垃圾收集吗?或者如果它仍在运行,会导致内存泄漏吗?

你可以看到runBlocking任何阻塞函数,比如Thread.sleep,没有更多的魔法了。就像Thread.sleep,runBlocking可以从任何函数调用,甚至是顶级函数,在这种情况下,不会涉及任何类实例。

让我们假设类中的一个方法调用runBlocking,并且内部的任何内容runBlocking都会挂起很长时间(例如长时间睡眠)。然后,无论谁调用此方法,都将保留对该实例的引用,直到该方法返回或失败,因此该实例无论如何都不会被垃圾收集。在这种情况下,调用者将挂起,阻塞它正在运行的任何线程——这就是可能发生泄漏的地方。

runBlocking 问题的示例

弹簧控制器

runblocking如果您在 Spring MVC 控制器的方法中使用,Spring 可能会为每个方法创建线程,如果有任何内容挂在内部runBlocking,您可能会泄漏线程。相反,您可以使用 Spring WebFlux,它允许您直接在 Spring 控制器中使用suspend函数(并添加请求超时以自动取消挂起的内容)。

回调

使用runBlocking内部回调也可能很危险。您正在使用的假设的基于回调的 API 可能正在使用线程池来回调您,如果runBlocking挂起,则可能会阻塞该线程池。

如果该 API 支持背压,那么阻塞可能是可以的。API 还可以以非阻塞方式支持这一点(例如 JDK11 的websocket 侦听器,它允许您CompletableStage从回调中返回 a),在这种情况下,您应该构建 aFuture而不是阻塞。

如果 API 不支持背压,您可能不得不在其中创建自定义的CoroutineScopeand launch-ing 协程来处理回调(而不是runBlocking)。当您不再使用基于回调的 API 时,您必须手动取消该范围。