c-a*_*-an 9 kotlin kotlin-coroutines
我正在研究CPS。我想知道它是如何工作的。
Object createPost(
Token token,
Item item,
Continuation<Post> const){...}
Run Code Online (Sandbox Code Playgroud)
interface Continuation<in T> {
val context: CoroutineContext
fun resume(value: T)
fun resumeWithException(exception: Throwable)
}
Run Code Online (Sandbox Code Playgroud)
人们说 CPS 只是回调,仅此而已。
但
<in T>Continuation 界面中做了什么。bro*_*oot 21
最终用户视角
对于最终用户来说,情况相对简单:延续代表被挂起的执行流。resume()它允许通过调用或来恢复执行resumeWithException()。
例如,假设我们要暂停一秒钟然后恢复执行。我们要求协程机制挂起,它提供一个延续对象,我们存储它,稍后我们调用resume()它。延续对象“知道”如何恢复执行:
suspend fun foo() {
println("foo:1")
val result = suspendCoroutine { cont ->
thread {
Thread.sleep(1000)
cont.resume("OK")
}
}
println("foo:2:$result")
}
Run Code Online (Sandbox Code Playgroud)
suspendCoroutine()是暂停并获取继续以稍后恢复的可能方法之一。thread()仅Thread.sleep()用于演示目的 - 通常我们应该使用delay()。
很多时候我们会停下来获取某种数据。这就是连续性支持使用结果值恢复的原因。在上面的示例中,我们可以看到 的结果suspendCoroutine()存储为result,并且我们通过传递 来恢复延续"OK"。这样恢复result持有后"OK"。这就解释了<in T>。
内部结构
这要复杂得多。Kotlin 在不支持协程或挂起的运行时中执行。例如,JVM 无法在不阻塞任何线程的情况下在函数内等待。这根本不可能(我在这里故意忽略 Project Loom)。为了实现这一点,Kotlin 编译器必须操作字节码,并且延续在这个过程中扮演着重要的角色。
正如您所注意到的,每个挂起函数都会接收额外的Continuation类型参数。该对象用于控制恢复的过程,它有助于返回到函数调用者并保存当前的协程上下文。此外,挂起函数返回Any/Object以允许向调用者发出其状态信号。
假设我们有另一个函数调用第一个函数:
suspend fun bar() {
println("bar:1")
foo()
println("bar:2")
}
Run Code Online (Sandbox Code Playgroud)
然后我们调用bar(). foo()两者的字节码bar()比您通过查看上面的源代码所预期的要复杂得多。这就是正在发生的事情:
bar()是通过其调用者的延续来调用的(让我们暂时忽略这意味着什么)。bar()检查它是否“拥有”传递的延续。它没有看到,所以它假设这是其调用者的延续,并且这是 的初始执行bar()。bar()创建自己的延续对象并将调用者的延续存储在其中。bar()开始正常执行并到达foo()目的。bar()调用foo()传递其延续。foo()检查它是否拥有传递的延续。事实并非如此,延续由 拥有bar(),因此foo()创建自己的延续,bar()将 的延续存储在其中并开始正常执行。suspendCoroutine()并与之前类似,本地状态存储在foo()的延续内部。foo()在传递给 的 lambda 内提供给最终用户suspendCoroutine()。foo()想要暂停其执行,所以它...返回...是的,如前所述,在不阻塞线程的情况下等待是不可能的,因此释放线程的唯一方法是从函数返回。foo()返回一个特殊值,表示:“执行已暂停”。bar()读取这个特殊值并且也暂停,所以也立即返回。cont.resume().foo()知道如何从该点继续执行suspendCoroutine()。foo()自身作为参数传递的函数。foo()检查它是否拥有传递的延续 - 这次它拥有,因此它假设这不是对 的初始调用foo(),而是恢复执行的请求。它从延续中读取存储的状态,加载局部变量并跳转到正确的代码偏移量。foo()to返回的点bar()。foo()知道这次它不是由 调用的bar(),所以简单地返回是行不通的。但它仍然保留其调用者的延续,因此在需要返回的bar()地方暂停。foo()foo()返回具有神奇值的内容:“恢复我的调用者的继续”。bar()从执行点恢复foo()。如您所见,这非常复杂。通常,协程的用户不需要了解它们的内部工作原理。
其他重要注意事项:
foo()不暂停,它将正常返回bar()并bar()继续照常执行。这是为了在不需要挂起的情况下减少整个过程的开销。CoroutineContext,因此也存储在延续内部。| 归档时间: |
|
| 查看次数: |
5483 次 |
| 最近记录: |