kotlin - Kotlin 协程如何在内部工作?

标签 kotlin kotlin-coroutines

Kotlin 如何在内部实现协程?

协程被称为线程的“轻量级版本”,据我了解,它们在内部使用线程来执行协程。

当我使用任何构建器函数启动协程时会发生什么?

这是我对运行这段代码的理解:

GlobalScope.launch {       <---- (A)
    val y = loadData()     <---- (B)  // suspend fun loadData() 
    println(y)             <---- (C)
    delay(1000)            <---- (D)
    println("completed")   <---- (E)
}
  1. Kotlin 开头有一个预定义的 ThreadPool
  2. (A) 处,Kotlin 开始在下一个可用的空闲线程(比如 Thread01)中执行协程。
  3. (B)处,Kotlin 停止执行当前线程,并在下一个可用的空闲线程(Thread02)。
  4. (B) 执行后返回时,Kotlin 在下一个可用的空闲线程(Thread03)中继续协程。
  5. (C)Thread03 上执行。
  6. (D) 处,Thread03 停止。
  7. 1000 毫秒后,(E) 在下一个空闲线程上执行,比如 Thread01

我理解正确吗?还是协程以不同的方式实现?


2021 年更新: Here's an excellent article由 Manuel Vivo 提供,补充了以下所有答案。

最佳答案

协程与您描述的任何调度策略完全不同。协程基本上是 suspend fun 的调用链。暂停完全由您控制:您只需调用 suspendCoroutine。您将获得一个回调对象,因此您可以调用它的 resume 方法并返回到您暂停的位置。

这里有一些代码,你可以看到暂停是一个非常直接和透明的机制,完全在你的控制之下:

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

var continuation: Continuation<String>? = null

fun main(args: Array<String>) {
    val job = GlobalScope.launch(Dispatchers.Unconfined) {
        while (true) {
            println(suspendHere())
        }
    }
    continuation!!.resume("Resumed first time")
    continuation!!.resume("Resumed second time")
}

suspend fun suspendHere() = suspendCancellableCoroutine<String> {
    continuation = it
}

上面的所有代码都在同一个主线程上执行。根本没有多线程。

launch 的协程每次调用suspendHere() 时都会自行挂起。它将延续回调写入 continuation 属性,然后您显式使用该延续来恢复协程。

代码使用 Unconfined 协程调度器,它根本不分派(dispatch)到线程,它只是在您调用 continuation.resume() 的地方运行协程代码。


考虑到这一点,让我们重新审视您的图表:

GlobalScope.launch {       <---- (A)
    val y = loadData()     <---- (B)  // suspend fun loadData() 
    println(y)             <---- (C)
    delay(1000)            <---- (D)
    println("completed")   <---- (E)
}
  1. Kotlin has a pre-defined ThreadPool at the beginning.

它可能有也可能没有线程池。 UI 调度程序与单个线程一起工作。

线程成为协程调度器目标的先决条件是有一个与之关联的并发队列,并且该线程运行一个顶级循环,该循环从该队列中获取 Runnable 对象,并且执行它们。协程调度器只是将延续放在该队列中。

  1. At (A), Kotlin starts executing the coroutine in the next available free thread (Say Thread01).

它也可以是你调用 launch 的同一个线程。

  1. At (B), Kotlin stops executing the current thread, and starts the suspending function loadData() in the next available free thread (Thread02).

Kotlin 不需要为了挂起协程而停止任何线程。事实上,协程的主要观点是线程不会启动或停止。线程的顶级循环将继续并选择另一个 runnable 来运行。

此外,您调用 suspend fun 的事实没有任何意义。协程只有在显式调用 suspendCoroutine 时才会挂起。该函数也可以简单地返回而不暂停。

但我们假设它确实调用了 suspendCoroutine。在这种情况下,协程不再在任何线程上运行。它被暂停并且无法继续,直到某个代码在某处调用continuation.resume()。该代码可以在未来任何时间在任何线程上运行。

  1. When (B) returns after execution, Kotlin continues the coroutine in the next available free thread (Thread03).

B 不会“执行后返回”,协程仍在其主体内时恢复。它可以在返回之前暂停和恢复任意次数。

  1. (C) executes on Thread03.
  2. At (D), the Thread03 is stopped.
  3. After 1000ms, (E) is executed on the next free thread, say Thread01.

再一次,没有线程被停止。协程被挂起,并且通常特定于调度程序的机制用于安排其在 1000 毫秒后恢复。此时它将被添加到与调度程序关联的运行队列中。


为了具体起见,让我们看一些示例,说明调度协程需要什么样的代码。

Swing UI 调度程序:

EventQueue.invokeLater { continuation.resume(value) }

Android UI 调度程序:

mainHandler.post { continuation.resume(value) }

ExecutorService 调度器:

executor.submit { continuation.resume(value) } 

关于kotlin - Kotlin 协程如何在内部工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53526556/

相关文章:

android - 如何使用 Dagger2 在 Android 中注入(inject) LifecycleOwner?

android - 在 Alert Dialog Builder 中启动协程

kotlin - 如何等待函数调用?

kotlin - StateFlow 与应用程序的生命周期相关,但没有 GlobalScope(Android、协程)

android - Room Dao LiveData 作为返回类型导致编译时错误

android - SharedFlow 不从发射中收集

javafx - Tornadofx onFocus监听器

android - Fragment 和 Anko toast 的“接收器类型不匹配”

Kotlin:使用对象与类来确定范围

kotlin - Foo::class.java 和 Foo::javaClass 有什么区别?