【问题标题】:How does a Coroutine Continuation internally work?协程延续在内部是如何工作的?
【发布时间】:2020-03-19 03:57:30
【问题描述】:

我已经使用协程工作了几个星期,有时很难理解线程并发和协程并发之间的真正工作差异。

挂起函数如何在内部工作?延续块如何帮助在暂停后恢复计算。 协程内代码行的顺序计算如何不阻塞线程?它比线程并发更好吗?

【问题讨论】:

    标签: android kotlin concurrency kotlin-coroutines


    【解决方案1】:

    挂起函数在内部是如何工作的?

    简而言之,在 Java 平台上,suspend fun 编译为与普通函数截然不同的字节码。它接收一个隐藏的额外参数(延续),创建自己的延续对象,整个函数体(大约)实现为一个大的switch 语句,允许函数在恢复时跳转到函数体的中间.

    suspend fun 挂起时,底层Java 方法实际上会返回。返回值是一个特殊的COROUTINE_SUSPENDED 单例对象,框架知道如何解释它。 suspend fun 本身有责任将延续对象保存在函数的结果准备好时可以访问的位置。

    official documentation 对这些细节有很好的深入描述。

    延续块如何帮助暂停后恢复计算。

    这与我上面所说的有关,suspend fun 本身负责确保它稍后恢复。它必须在函数suspendCoroutineOrReturn 提供的块内执行此操作。用户代码不会直接调用它,而是更高级的类似物suspendCoroutinesuspendCancellableCoroutine。这些接管了在适当的线程上恢复协程的问题,开发人员只负责确保在结果可用时调用continuation.resume()。这通常发生在您传递给异步调用的回调中。

    您可以学习this answer,它试图在一个独立的示例中解释暂停-恢复机制。

    协程内部代码行的顺序计算如何不阻塞线程?

    因为它实际上是从函数编译成返回,然后通过跳转到函数体的中间来恢复。

    它比线程并发好在哪里?

    本机线程是重量级资源,需要时间来创建和销毁。协程的重量要轻得多,因此您可以更快地启动更多协程。

    【讨论】:

    • Marko 与您关于线程性能的最后一点有关,我希望对此有更清楚的说明,所以您的意思是说线程具有上下文切换,而协程没有?
    • “上下文切换”是操作系统所做的事情,它涉及进入内核模式(这是一项昂贵的操作)。然后操作系统找到另一个线程来恢复,设置它的堆栈和寄存器内容,并切换回用户模式(另一个昂贵的操作)。当一个协程挂起而调度程序恢复另一个协程时,这些都不会发生,这只是 Java 方法调用。
    【解决方案2】:

    内部工作原理在原始设计文档https://github.com/Kotlin/KEEP/blob/master/proposals/coroutines.md 中进行了说明,其中有一个关于“实施细节”的部分。

    【讨论】:

    • 相当长的文档,但有助于理解协程工作的基本结构。
    猜你喜欢
    • 2019-04-30
    • 2020-09-15
    • 2018-12-15
    • 2021-12-06
    • 1970-01-01
    • 2021-07-22
    • 2015-03-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多