【问题标题】:If I write blocking code inside a Kotlin coroutine will it still block the thread?如果我在 Kotlin 协程中编写阻塞代码,它还会阻塞线程吗?
【发布时间】:2021-09-23 18:08:04
【问题描述】:

这个问题是在与同事广泛讨论后提出的,

问题如下:

在suspendCoroutine块中调用同步方法之前我在做的事情:

private suspend fun <T : Any> request(queryRequest: GraphQLQueryRequest<T>): T? = suspendCoroutine { continuation ->
    val response = graphqlClient.executeQuery(queryRequest.query, queryRequest.variables, executor)
    val item = response.extractValueAsObject("data.${queryRequest.pathAccessor}", queryRequest.type.java)
    continuation.resume(item)
}

他声称我的代码没有阻止线程被阻塞,整个过程必须等待我的协程恢复才能释放线程。

他的提议:

private suspend fun <T : Any> request(queryRequest: GraphQLQueryRequest<T>): T? {
    val response = graphqlClient.reactiveExecuteQuery(queryRequest.query, queryRequest.variables, executor)
    return response.awaitSingle().extractValueAsObject("data.${queryRequest.pathAccessor}", queryRequest.type.java)
}

reactiveExecuteQuery() 将返回 WebFlux Mono,然后我们调用.awaitSingle() 将 Mono 转换为挂起函数,他声称这种方法与第一种不同,不会阻塞线程,一切都会正常工作。

根据documentation的suspendCoroutine

Obtains the current continuation instance inside suspend functions and suspends the currently running coroutine.

是否有人对 Kotlin 协程有更深入的了解,是否知道这些解决方案是否真的提供了不同的结果?就没有阻塞线程的阻塞而言?

如果这不是 suspendCoroutine 的用例,那会是什么?

提前感谢您的任何回复:)

【问题讨论】:

  • 你使用的graphql客户端是什么?如果 executeQuery 不是挂起函数,那么您的同事是对的。你的解决方案会阻塞一个线程。
  • suspendCoroutine 用于将基于回调的 API 转换为协程
  • “不要阻塞事件总线”——我认为阻塞协程不是一个好的设计。让 Kotlin 管理它们。

标签: kotlin asynchronous spring-webflux kotlin-coroutines suspend


【解决方案1】:

正如@Martin Tarjányi 已经提到的,您的同事总体上是对的。

awaitSingle 是一个挂起函数,你可以在documentation 中看到。这意味着调用它会挂起协程,但其他协程仍然可以在同一个 Dispatcher 上运行。

有时,没有其他解决方案,当一个库只提供阻塞 API 时,你应该在一个相对较大的线程池上运行你的协程。使用您的代码,您将在调用 request() 的同一调度程序上运行,通常是 Dispatchers.Default,这等于您的内核数(如果您在单核 CPU 上运行,则为 2)。

【讨论】:

  • 当你不得不调用阻塞代码时,你可以通过将它包装在withContext(Dispatchers.IO)中来缓解这个问题,因为IO有一个相对较大的线程池。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-21
  • 1970-01-01
  • 2016-07-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多