挂起函数中的阻塞与调用异步函数有何不同?
How is blocking in a suspend function different than calling an async function?
我正在使用 OkHttp 发出同步 http 请求。为了避免阻塞主线程,我将阻塞的网络调用包装在一个挂起函数中,withContext(Dispatchers.IO)
suspend fun run(): String {
val client = OkHttpClient()
val request = Request.Builder()
.url("https://publicobject.com/helloworld.txt")
.build()
return withContext(Dispatchers.IO) {
val response = client.newCall(request).execute()
return@withContext "Request completed successfully"
}
}
Android Studio 警告我 execute()
是“不适当的阻止方法调用”。我的理解是 execute()
将在 http 请求期间阻塞,在请求期间占用 Dispatchers.IO 中的一个线程,这并不理想。为了避免这个问题,我可以使用包裹在 suspendCoroutine
中的请求的异步版本
suspend fun runAsync(): String = suspendCoroutine { continuation ->
val client = OkHttpClient()
val request = Request.Builder()
.url("http://publicobject.com/helloworld.txt")
.build()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
continuation.resumeWithException(e)
}
override fun onResponse(call: Call, response: Response) {
response.use {
if (!response.isSuccessful) throw IOException("Unexpected code $response")
continuation.resume("Request completed successfully")
}
}
})
}
这避免了警告,但我不明白它与上面的同步版本在功能上有何不同。我假设 http 调用的异步版本使用线程来等待请求。这个假设是否正确?如果不是,异步函数如何等待回调到return?
I do not understand how it is functionally different than the synchronous version above. I am assuming that the async version of the http call uses a thread to wait on the request. Is this assumption correct? If not, how does the async function wait for the callback to return?
这是不正确的,异步方法的要点是在调用发生时没有线程被阻塞。您的第二种方法实现了这一点——当协程挂起时,它根本不存在于任何线程上。根据 OkHttp
的实现细节,某些内部线程可能会或可能不会被阻塞,因为它依赖于阻塞 IO。理想情况下,它应该根据 Java NIO(更典型地,通过 Netty 库)来实现,并依赖于称为选择器的低级非阻塞原语。
正确的方法是您实现暂停的方式,除了正如用户 Tenfour04 指出的那样,OkHttpClient 已经在其 await()
函数中提供了完全相同的实现。
我正在使用 OkHttp 发出同步 http 请求。为了避免阻塞主线程,我将阻塞的网络调用包装在一个挂起函数中,withContext(Dispatchers.IO)
suspend fun run(): String {
val client = OkHttpClient()
val request = Request.Builder()
.url("https://publicobject.com/helloworld.txt")
.build()
return withContext(Dispatchers.IO) {
val response = client.newCall(request).execute()
return@withContext "Request completed successfully"
}
}
Android Studio 警告我 execute()
是“不适当的阻止方法调用”。我的理解是 execute()
将在 http 请求期间阻塞,在请求期间占用 Dispatchers.IO 中的一个线程,这并不理想。为了避免这个问题,我可以使用包裹在 suspendCoroutine
suspend fun runAsync(): String = suspendCoroutine { continuation ->
val client = OkHttpClient()
val request = Request.Builder()
.url("http://publicobject.com/helloworld.txt")
.build()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
continuation.resumeWithException(e)
}
override fun onResponse(call: Call, response: Response) {
response.use {
if (!response.isSuccessful) throw IOException("Unexpected code $response")
continuation.resume("Request completed successfully")
}
}
})
}
这避免了警告,但我不明白它与上面的同步版本在功能上有何不同。我假设 http 调用的异步版本使用线程来等待请求。这个假设是否正确?如果不是,异步函数如何等待回调到return?
I do not understand how it is functionally different than the synchronous version above. I am assuming that the async version of the http call uses a thread to wait on the request. Is this assumption correct? If not, how does the async function wait for the callback to return?
这是不正确的,异步方法的要点是在调用发生时没有线程被阻塞。您的第二种方法实现了这一点——当协程挂起时,它根本不存在于任何线程上。根据 OkHttp
的实现细节,某些内部线程可能会或可能不会被阻塞,因为它依赖于阻塞 IO。理想情况下,它应该根据 Java NIO(更典型地,通过 Netty 库)来实现,并依赖于称为选择器的低级非阻塞原语。
正确的方法是您实现暂停的方式,除了正如用户 Tenfour04 指出的那样,OkHttpClient 已经在其 await()
函数中提供了完全相同的实现。