Kotlin 如何在内部实现协程?
协程被称为线程的“轻量级版本”,据我了解,它们在内部使用线程来执行协程。
当我使用任何构建器函数启动协程时会发生什么?
这是我对运行这段代码的理解:
GlobalScope.launch { <---- (A)
val y = loadData() <---- (B) // suspend fun loadData()
println(y) <---- (C)
delay(1000) <---- (D)
println("completed") <---- (E)
}
- Kotlin 开头有一个预定义的
ThreadPool
。 - 在
(A)
处,Kotlin 开始在下一个可用的空闲线程(比如Thread01
)中执行协程。 - 在
(B)
处,Kotlin 停止执行当前线程,并在下一个可用的空闲线程(Thread02
)。 - 当
(B)
执行后返回时,Kotlin 在下一个可用的空闲线程(Thread03
)中继续协程。 (C)
在Thread03
上执行。- 在
(D)
处,Thread03
停止。 - 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)
}
- Kotlin has a pre-defined
ThreadPool
at the beginning.
它可能有也可能没有线程池。 UI 调度程序与单个线程一起工作。
线程成为协程调度器目标的先决条件是有一个与之关联的并发队列,并且该线程运行一个顶级循环,该循环从该队列中获取 Runnable
对象,并且执行它们。协程调度器只是将延续放在该队列中。
- At
(A)
, Kotlin starts executing the coroutine in the next available free thread (SayThread01
).
它也可以是你调用 launch
的同一个线程。
- At
(B)
, Kotlin stops executing the current thread, and starts the suspending functionloadData()
in the next available free thread (Thread02
).
Kotlin 不需要为了挂起协程而停止任何线程。事实上,协程的主要观点是线程不会启动或停止。线程的顶级循环将继续并选择另一个 runnable 来运行。
此外,您调用 suspend fun
的事实没有任何意义。协程只有在显式调用 suspendCoroutine
时才会挂起。该函数也可以简单地返回而不暂停。
但我们假设它确实调用了 suspendCoroutine
。在这种情况下,协程不再在任何线程上运行。它被暂停并且无法继续,直到某个代码在某处调用continuation.resume()
。该代码可以在未来任何时间在任何线程上运行。
- When
(B)
returns after execution, Kotlin continues the coroutine in the next available free thread (Thread03
).
B
不会“执行后返回”,协程仍在其主体内时恢复。它可以在返回之前暂停和恢复任意次数。
(C)
executes onThread03
.- At
(D)
, theThread03
is stopped.- After 1000ms,
(E)
is executed on the next free thread, sayThread01
.
再一次,没有线程被停止。协程被挂起,并且通常特定于调度程序的机制用于安排其在 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/