Kotlin协程如何知道何时进行网络呼叫?

Raf*_*oul 12 java coroutine kotlin kotlin-coroutines

我是Kotlin协同程序的新手,有一点我没想到的是,协同程序如何知道在进行网络调用时何时屈服于其他人.

如果我理解正确的话,协同程序会先发制人地工作,这意味着当它执行一些耗时的任务(通常是I/O操作)时,它知道何时屈服于其他协同程序.

例如,假设我们想绘制一些将显示来自远程服务器的数据的UI,并且我们只有一个线程来安排我们的协同程序.我们可以启动一个协程来进行REST API调用以获取数据,同时让另一个协同程序绘制UI的其余部分,它们不依赖于数据.但是,由于我们只有一个线程,因此一次只能运行一个协程.除非用于获取数据的协程在等待数据到达时抢先获取,否则两个协同程序将按顺序执行.

据我所知,Kotlin的协程实现并没有修补任何现有的JVM实现或JDK网络库.因此,如果协程正在调用REST API,它应该像使用Java线程一样阻止.我这样说是因为我在python中看起来有类似的概念,叫做绿色线程.为了使它能够与python的内置网络库一起工作,必须首先对网络库进行"猴子修补".对我而言,这是有道理的,因为只有网络库本身知道何时屈服.

那么,任何人都可以解释Kotlin协程如何知道何时调用阻塞Java网络API?或者如果没有,那么它是否意味着上面例子中提到的任务无法同时执行给出单个线程?

谢谢!

Mar*_*nik 6

协程先发制人

不。使用协程,您只能实现协作式多线程,在其中通过显式方法调用挂起和恢复协程。协程仅挑出了按需暂停和恢复的问题,而协程调度程序负责确保它在适当的线程上启动和恢复。

学习此代码将帮助您了解Kotlin协程的本质:

import kotlinx.coroutines.experimental.*
import kotlin.coroutines.experimental.*

fun main(args: Array<String>) {
    var continuation: Continuation<Unit>? = null
    println("main(): launch")
    GlobalScope.launch(Dispatchers.Unconfined) {
        println("Coroutine: started")
        suspendCoroutine<Unit> {
            println("Coroutine: suspended")
            continuation = it
        }
        println("Coroutine: resumed")
    }
    println("main(): resume continuation")
    continuation!!.resume(Unit)
    println("main(): back after resume")
}
Run Code Online (Sandbox Code Playgroud)

在这里,我们使用最简单的Unconfined调度程序,它不执行任何调度,而是在您调用launch { ... }和的地方直接运行协程continuation.resume()。协程通过调用暂停自身suspendCoroutine。该函数通过将您提供的块传递给您以后可以用来恢复协程的对象来运行它。我们的代码将其保存到中var continuation。控件返回到之后的代码launch,在这里我们使用延续对象恢复协程。

整个程序在主线程上执行并输出:

main(): launch
Coroutine: started
Coroutine: suspended
main(): resume continuation
Coroutine: resumed
main(): back after resume
Run Code Online (Sandbox Code Playgroud)

我们可以启动一个协程进行REST API调用以获取数据,而让另一个协程绘制不依赖数据的UI其余部分。

这实际上描述了您将对普通线程执行的操作。协程的优点在于,您可以在GUI绑定的代码中间进行“阻止”调用,并且不会冻结GUI。在您的示例中,您将编写一个进行网络调用的协程,然后更新GUI。在进行网络请求时,协程将被挂起,其他事件处理程序将运行,从而使GUI保持活动状态。处理程序不是协程,它们只是常规的GUI回调。

用最简单的术语来说,您可以编写以下Android代码:

activity.launch(Dispatchers.Main) {
    textView.text = requestStringFromNetwork()
}

...

suspend fun requestStringFromNetwork() = suspendCancellableCoroutine<String> {
    ...
}
Run Code Online (Sandbox Code Playgroud)

requestStringFromNetwork相当于“修补IO层”,但实际上并没有修补任何内容,只是围绕IO库的公共API编写包装器。几乎所有Kotlin IO库都添加了这些包装器,并且还有Java IO库的扩展库。如果遵循这些说明,编写自己的代码也非常简单。

  • 因此,这就是我现在的理解。1.使用Kotlin协程不会自动将阻塞调用变成非阻塞调用,它只是提供了一种更简单,更自然的编写异步代码的方式(通过提供暂停和恢复协程的机制)。2.为了充分利用它,我们最好使用某种异步库,例如Java NIO或具有回调的CompletableFuture。3.另一方面,如果使用阻塞调用,除非使用更多线程,否则我们仍然会阻塞。@Marko,能否请您核实我的新理解是否正确?谢谢! (3认同)
  • 是的,几乎可以达到目标。阻塞调用总是以“本机”方法结束,因此您无法从外部对它们进行修补以将其变为非阻塞调用。Python在这里有很大的不同,因为它的IO始终基于非阻塞IO和IO事件循环。这就是您_can_修补程序以获得类协程行为的原因。 (3认同)