协同程序中的IO是否导致暂停?

时间:2018-04-04 13:53:11

标签: kotlin kotlin-coroutines

在协程中我正在使用OkHttpClient进行http请求。请求是通过具有suspend关键字的函数完成的:

suspend fun doSomethingFromHttp(someParam:String): Something {
    ...
    val response = HttpReader.get(url)
    return unmarshalSomething(response)!!
}

我认为该函数可以在入口时暂停,因为它具有suspend关键字,但在执行http请求时协程是否也会被挂起?那么其他类型的阻塞IO呢?

3 个答案:

答案 0 :(得分:15)

Kotlin协程没有自动化。如果您调用HttpReader.get()之类的阻止功能,协程将无法暂停,相反,呼叫将被阻止。你可以很容易地向自己保证,给定的函数不会导致协程暂停:如果它不是suspend函数,它就不可能这样做,无论它是否被调用来自suspend函数。

如果要将现有阻止API转换为非阻塞,可挂起的调用,则必须将阻塞调用提交给线程池。实现它的最简单方法如下:

val response = withContext(Dispatchers.IO) { HttpReader.get(url) }

withContextsuspend fun,它将暂停协程,将提供的块提交给另一个协同调度程序(此处为IO),并在该块完成时恢复并提出其结果

您还可以轻松地实例化自己的ExecutorService并将其用作协程调度程序:

val myPool = Executors.newCachedThreadPool().asCoroutineDispatcher()

现在你可以写

val response = withContext(myPool) { HttpReader.get(url) }

这样,您可以处理受您控制的线程池,而不是全局IO。请记住,您现在也对close()负责。

答案 1 :(得分:0)

此PR具有适当的OkHttp协程支持的示例代码

https://github.com/square/okhttp/pull/4129/files

它使用OkHttp的线程池进行工作。代码的关键是这个通用库代码。

 suspend fun OkHttpClient.execute(request: Request): Response {
   val call = this.newCall(request)
   return call.await()
 }

 suspend fun Call.await(): Response {
  return suspendCancellableCoroutine { cont ->
    cont.invokeOnCancellation {
      cancel()
    }
    enqueue(object : Callback {
      override fun onFailure(call: Call, e: IOException) {
        if (!cont.isCancelled) {
          cont.resumeWithException(e)
        }
      }
       override fun onResponse(call: Call, response: Response) {
        if (!cont.isCancelled) {
          cont.resume(response)
        }
      }
    })
  }
}

答案 2 :(得分:0)

JAVA世界中有两种类型的IO库,使用IO或NIO。

您可以在https://dzone.com/articles/high-concurrency-http-clients-on-the-jvm

中找到更多文档。

使用NIO的程序在理论上可以提供真正的非阻塞挂起,而与IO程序不同,后者仅将任务卸载到单独的线程中。

NIO使用JVM中的一些调度程序线程来使用多路复用(反应堆设计模式)处理输入输出套接字。它的工作方式是,我们请NIO /调度程序加载/卸载某些内容,然后它们会给我们将来的参考。可以轻松地将此代码转换为协程。

对于基于IO的库,协程实现不是真正的非阻塞。它实际上就像Java中一样阻塞线程之一,但是通常的使用模式是使用Dispatcher.IO,它是用于阻塞IO任务的线程池。

建议不要使用https://ktor.io/docs/client.html

,而不要使用OkHttpClient。