挂起函数中的阻塞与调用异步函数有何不同?

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() 函数中提供了完全相同的实现。