如何理解 Kotlin 协程?

cha*_*onk 8 asynchronous async-await kotlin kotlin-coroutines

我曾尝试阅读有关 Kotlin 协程的各种教程和页面,尽管这对我来说很有意义,但我仍然觉得它没有点击,我还没有准备好开始使用协程编写异步非阻塞代码。我认为我缺少的是一个图表或图片,说明当一段协程代码执行时究竟发生了什么以及以什么顺序执行。该代码如何在线程级别运行?

    launch {
        delay(1000)
        println("World (${currentThread().name})")
    }
    println("Hello (${currentThread().name})")
    sleep(1500)
Run Code Online (Sandbox Code Playgroud)

我的理解是这样的。我很高兴得到纠正或给出一个不同的例子来进一步理解。

Line0:代码从主线程开始

Line1:在一个新线程上启动一个新的协程(我想来自 forkjoin 池)

Line2:暂停函数,因此协程暂停并将线程返回到线程池(因此是非阻塞的)

Line5:在主线程上打印

Line6:阻塞主线程1.5s

Line3:协程继续执行(不确定这里是哪个线程 - 与暂停前的线程相同还是可以是不同的线程?)。协程在该线程上打印并完成,因此再次将线程返回到池中。

我的另一个问题是,如果我将整个代码包装起来,低级执行将如何改变 runBlocking { ... }

Mar*_*nik 6

您的代码实际上并没有做任何可以揭示协程特殊性质的事情。它让两个线程同时做他们的事情,就像他们在普通 Java 中做的那样。

只有在您已经在的同一线程(例如,主线程)上启动协程时,它才会变得有趣。这是您使用runBlocking块实现的目标之一:

runBlocking {
    launch {
        delay(1000)
        println("Hello from the launched coroutine. My thread is "
                + Thread.currentThread().name)
    }
    println("Hello from the top-level coroutine. My thread is "
            + Thread.currentThread().name)
}
Run Code Online (Sandbox Code Playgroud)

这将打印

Hello from the top-level coroutine. My thread is main
Hello from the launched coroutine. My thread is main
Run Code Online (Sandbox Code Playgroud)

runBlocking在调用线程上运行一个事件循环,并将对它的引用传播到您在其中启动的所有协程。例如,delay(1000)将向此事件循环发布一个事件,指定延迟一秒,然后它会挂起协程。这将允许主线程运行下面的其余代码launch。当时间流逝时,事件循环将运行事件处理程序,这将依次恢复协程。


一个更有教育意义的例子是启动一个没有任何Dispatcher. 这消除了协程看起来像线程的错觉,并揭示了它们的真正魔力:通过调用,continuation.resume()您可以使当前线程直接跳转到挂起的协程代码块的中间,所有这些都使用普通的 Java 方法调用发生。我建议研究这个答案,其中详细解释了这一点。如果您对简单的 Java 方法如何实现这一技巧的更深入解释感兴趣,我建议您在 YouTube 上观看 Roman Elizarov 的解释


Rik*_*edi 1

这是这里提到的另一个例子:https ://kotlinlang.org/docs/reference/coroutines/basics.html#your-first-coroutine

fun main() {
    GlobalScope.launch { // launch a new coroutine in background and continue
        delay(1000L) // non-blocking delay for 1 second (default time unit is ms)
        println("World!") // print after delay
    }
    println("Hello,") // main thread continues while coroutine is delayed
    Thread.sleep(2000L) // block main thread for 2 seconds to keep JVM alive
}
Run Code Online (Sandbox Code Playgroud)

上面的代码打印:

Hello,
World!
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,虽然代码看起来是同步的,但执行是异步的。这就是协程的全部思想。