在协程中我正在使用OkHttpClient进行http请求。请求是通过具有suspend
关键字的函数完成的:
suspend fun doSomethingFromHttp(someParam:String): Something {
...
val response = HttpReader.get(url)
return unmarshalSomething(response)!!
}
我认为该函数可以在入口时暂停,因为它具有suspend
关键字,但在执行http请求时协程是否也会被挂起?那么其他类型的阻塞IO呢?
答案 0 :(得分:15)
Kotlin协程没有自动化。如果您调用HttpReader.get()
之类的阻止功能,协程将无法暂停,相反,呼叫将被阻止。你可以很容易地向自己保证,给定的函数不会导致协程暂停:如果它不是suspend
函数,它就不可能这样做,无论它是否被调用来自suspend
函数。
如果要将现有阻止API转换为非阻塞,可挂起的调用,则必须将阻塞调用提交给线程池。实现它的最简单方法如下:
val response = withContext(Dispatchers.IO) { HttpReader.get(url) }
withContext
是suspend 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任务的线程池。
,而不要使用OkHttpClient。