Kotlin协程的JVM实现源码分析(下)

news2024/12/27 13:41:58

协程 根据 是否保存切换 调用栈 ,分为:

  1. 有栈协程(stackful coroutine)
  2. 无栈协程(stackless coroutine)

在代码上的区别是:是否可在普通函数里调用,并暂停其执行。

Kotlin协程,必须在挂起函数中调用和恢复,属于 无栈协程

常见的语言,协程实现:

  • 有栈协程:Go、Lua
  • 无栈协程:Kotlin、C++ 20、Clojure、JavaScript

二、无栈协程 和 Continuation

2.1 CPS(Continuation-passing-style)

在上篇源码分析中,不难发现 执行的结果,都是通过 Continuation 来返回。

2.1.1 Continuation

Continuation 就是 一个通用的回调接口,返回 Result<T> 值 或 异常。

Continuation is a generic callback interface. —— Roman Elizarov

public interface Continuation<in T> {

    public val context: CoroutineContext

    public fun resumeWith(result: Result<T>)
}
2.1.2 CPS

挂起函数 调用 其他挂起函数时,会将自己的 Continuation对象 作为 completion 参数 传递,
这种传递Continuation的方式,称为 连续传递风格(Continuation-passing-style),简称为 CPS

挂起函数 编译后,会创建基于 ContinuationImpl 对象,把 调用者Continuation 传给 completion 构造参数:

internal abstract class BaseContinuationImpl(
    public val completion: Continuation<Any?>?
)
2.1.3 Continuation结果返回

上篇知道 协程执行在 BaseContinuationImpl.resumeWith 方法,
同样 结果返回逻辑 也在这里,看下代码:

和 传递逻辑顺序 相反,结果按 逐步向上 返回。

resumeWith

分析:当获取结果后,通过 while 循环,completion 将结果向上传递,一般是协程 StandaloneCoroutine 作为最终的 completion 完成结果回调。

2.2 状态机

无栈协程,是通过 状态机状态 保存恢复 来实现协程挂起恢复。

和 每个 回调 都要创建 回调对象 相比,状态机 通过 状态 记录 执行位置,

当 挂起函数完成后,只需 恢复状态 接着执行后面的代码。

其实就是通过 switch(label) 做判断,判断位置执行。

状态机 vs 回调,有以下几个优点:

  1. 复用 方法对象和状态,避免每次分配对象
  2. 简化 循环 和 使用 高阶函数

以下面 请求解析数据 为例,launch {} 对应的 lambda挂起函数 ,分析 Kotlin 状态机状态:

GlobalScope.launch {
  // 挂起点1
  val data = getData()
  // 挂起点2
  val result = parseData(data)
  println("data: $data, result: $result")
}

Kotlin编译后逻辑,以 伪代码 表示:

class $main$1 extends SuspendLambda {
  // 挂起点的位置
  int label;
  // 状态 对象 保存 和 恢复
  Object L$0;
  // 更多状态: L$1 L$2 ...

  Object invokeSuspend(Object result) {
    Object obj;
    switch (this.label) {
      case 0:
        this.label = 1;
        obj = getData(this);
        // 表示挂起,存储 状态 label = 1,
        // 恢复时再次调用 invokeSuspend,恢复执行下面
        if (obj == COROUTINE_SUSPENDED) {
          return COROUTINE_SUSPENDED;
        }
        // 没有break,如果没有挂起,直接 执行下面的过程

      case 1:
		// 挂起恢复后
		String data = (String) result;
		// 如果没有挂起,直接执行则是:
		// String data = (String) obj;
        this.label = 2;
        // 保存 状态
        this.L$0 = data;
        obj = parseData(data, this);
        if (obj == COROUTINE_SUSPENDED) {
          return COROUTINE_SUSPENDED;
        }

      case 2:
		// 挂起恢复后
		Integer num = (Integer) result;
		// 如果没有挂起,直接执行则是:
		// Integer num = (Integer) obj;
		// 恢复状态
        String data = (String) this.L$0;
        System.out.println("data: " + data + ",num: " + num);
        return Unit.INSTANCE;

      default:
        throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
    }
    
  }
}

2.3 CPS Transform

上面说到调用挂起函数 continuation 会作为函数参数传递,但是 声明挂起函数时,
并没有 continuation参数。而是 Kotlin 会在参数列表 自动加上 Continuation 参数,这个操作叫做 CPS Transform

举例,下面挂起函数:

suspend fun <T> CompletableFuture<T>.await(): T

而在 CPS Transform 后,实际的代码是:

fun <T> CompletableFuture<T>.await(continuation: Continuation<T>): Any?

小结

  • Kotlin协程,通过 状态机 实现,复用闭包。
  • 挂起函数, 编译成 Continuation 回调对象,CPS。
  • suspend 以同步的编程方式,执行异步方法

文档

  • Coroutine | Wikipedia
  • KEEP | Kotlin
  • KotlinConf 2017 - Deep Dive into Coroutines on JVM
  • ContinuationImpl.kt
  • 为什么无栈协程不能被非协程函数嵌套调用? | 知乎
  • 浅谈有栈协程与无栈协程 | 知乎
  • 理解有栈无栈协程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1402456.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Apifox适用于API测试、管理的工具

一、产品介绍 Apifox是一款强大的API管理工具&#xff0c;它可以帮助开发人员和团队高效地设计、开发、测试、部署和管理API。Apifox提供了丰富的功能&#xff0c;如API文档生成、版本控制、团队协作、性能监控等&#xff0c;让API开发和管理变得更加简单和高效。 二、应用场…

2023 年值得一读的技术文章 | NebulaGraph 技术社区

在之前的产品篇&#xff0c;我们了解到了 NebulaGraph 内核及周边工具在 2023 年经历了什么样的变化。伴随着这些特性的变更和上线&#xff0c;在【文章】博客分类中&#xff0c;一篇篇的博文记录下了这些功能背后的设计思考和研发实践。当中&#xff0c;既有对内存管理 Memory…

【LeetCode】每日一题 2024_1_21 分割数组的最大值(二分)

文章目录 LeetCode&#xff1f;启动&#xff01;&#xff01;&#xff01;题目&#xff1a;分割数组的最大值题目描述代码与解题思路 LeetCode&#xff1f;启动&#xff01;&#xff01;&#xff01; 今天是 hard&#xff0c;难受&#xff0c;还好有题解大哥的清晰讲解 题目&a…

十一、K8S-ingress

目录 一、什么是Ingress 1、为什么要用ingress&#xff1a; 2、ingress概念&#xff1a; 1、pod漂移问题 ​编辑 2、端口管理的问题&#xff1a; 3、域名分配及动态更新问题 3、Ingress-nginx 工作原理 4、ingress-controller工作原理 5、ingress部署原理 1、Deploy…

【UEFI基础】EDK网络框架(TCP4)

TCP4 TCP4协议说明 相比UDP4&#xff0c;TCP4是一种面向连接的通信协议&#xff0c;因此有更好的可靠性。 TCP4的首部格式如下&#xff1a; 各个参数说明如下&#xff1a; 字段长度&#xff08;bit&#xff09;含义Source Port16源端口&#xff0c;标识哪个应用程序发送。D…

爬虫案例—爬取ChinaUnix.net论坛板块标题

爬虫案例—爬取ChinaUnix.net论坛板块标题 ChinaUnix.net论坛网址&#xff1a;http://bbs.chinaunix.net 目标&#xff1a;抓取各个板块的标题和内容的标题 网站截图&#xff1a; 利用requests和xpath实现目标。源码如下&#xff1a; import requests from lxml import etr…

Vue——计算属性

文章目录 计算属性computed 计算属性 vs methods 方法计算属性完整写法 综合案例&#xff1a;成绩案例 计算属性 概念&#xff1a;基于现有的数据&#xff0c;计算出来的新属性。依赖的数据变化&#xff0c;自动重新计算 语法: ①声明computed配置项中&#xff0c;一个计算属性…

vue3-模版引用ref

1. 介绍 概念&#xff1a;通过 ref标识 获取真实的 dom对象或者组件实例对象 2. 基本使用 实现步骤&#xff1a; 调用ref函数生成一个ref对象 通过ref标识绑定ref对象到标签 代码如下&#xff1a; 父组件&#xff1a; <script setup> import { onMounted, ref } …

必看——SSL安全证书

SSL&#xff08;Secure Socket Layer&#xff09;安全证书是一种用于确保在网络上数据传输过程中的安全性和加密性的数字证书。SSL证书通过对数据进行加密&#xff0c;确保敏感信息在用户和服务器之间的传输过程中不被窃取或篡改。下面是获取和配置SSL安全证书的基本步骤&#…

【大数据】YARN常用命令及Rest API

YARN 1.YARN常用命令 1.1 作业 命令说明yarn application -list列出所有的applicationyarn application -list -appStates [ALL、NEW、NEW_SAVING、SUBMITTED、ACCEPTED、RUNNING、FINISHED、FAILED、KILLED]根据application状态过滤yarn application -kill [applicationId]…

【GitHub项目推荐--不错的 C 开源项目】【转载】

大学时接触的第一门语言就是 C语言&#xff0c;虽然距 C语言创立已过了40多年&#xff0c;但其经典性和可移植性任然是当今众多高级语言中不可忽视的&#xff0c;想要学好其他的高级语言&#xff0c;最好是先从掌握 C语言入手。 今天老逛盘点 GitHub 上不错的 C语言 开源项目&…

commit 历史版本记录修正

commit 历史版本记录修正 当 Bug 发生的时候&#xff0c;我们会需要去追踪特定 bug 的历史记录&#xff0c;以查出该 bug 真正发生的原因&#xff0c;这个时候就是版本控制带来最大价值的时候。 因此&#xff0c;要怎样维持一个好的版本记录是非常重要的&#xff0c;下面是一…

第91讲:MySQL主从复制集群主库与从库状态信息的含义

文章目录 1.主从复制集群正常状态信息2.从库状态信息中重要参数的含义 1.主从复制集群正常状态信息 通过以下命令查看主库的状态信息。 mysql> show processlist;在主库中查询当前数据库中的进程&#xff0c;看到Master has sent all binlog to slave; waiting for more u…

通俗易懂理解小波池化/WaveCNet

重要说明&#xff1a;本文从网上资料整理而来&#xff0c;仅记录博主学习相关知识点的过程&#xff0c;侵删。 一、参考资料 github代码&#xff1a;WaveCNet 通俗易懂理解小波变换(Wavelet Transform) 二、相关介绍 关于小波变换的详细介绍&#xff0c;请参考另一篇博客&…

【工具与中间件】GitGitHub相关知识与一些问题解决、工具推荐

文章目录 前言1. Git 基础快速回顾1.1 Git 相关概念简单回顾1.2 Git 基本命令1.2.1 分支命令1.2.2 推拉命令 2. Git Hub 创建仓库3. 版本控制实战3.1 创建本地项目3.2 绑定远程仓库3.3 代码推拉3.3.1 推拉实战3.3.2 合并请求 4. 补充与总结4.1 可能会遇到的问题4.2 补充&#x…

Unity -简单键鼠事件和虚拟轴

简单键鼠事件 — “Test_03” KeyTest 键鼠事件每帧都要监听&#xff0c;要放在Update()中处理 public class KeyTest : MonoBehaviour {// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){// 【鼠标点击事件…

15.1_使用Verilog设计:一个简单的状态机设计——序列检测器(可实现重复性检测)

使用Verilog设计&#xff1a;一个简单的状态机设计——序列检测器&#xff08;可实现重复性检测&#xff09; 1&#xff0c;一个简单的状态机设计&#xff1a;可重复性序列检测器2&#xff0c;可重复性状态机序列检测实现2.1&#xff0c;RTL设计代码实现2.2&#xff0c;tb测试代…

加码OT安全丨Fortinet与施耐德电气携手共创工业零信任安全创新方案

近日&#xff0c;专注于推动网络与安全融合的全球网络安全领导者 Fortinet在施耐德电气举办的第四季“绿色智能制造创赢计划”结营仪式上&#xff0c;正式与其签署联创方案合作协议&#xff0c;成为施耐德电气“生态合作伙伴”。双方将依托“基于关键装置/设备的微隔离防护”这…

【RT-DETR有效改进】Google | EfficientNetV2一种超轻量又高效的网络 (轻量化网络)

前言 大家好&#xff0c;我是Snu77&#xff0c;这里是RT-DETR有效涨点专栏。 本专栏的内容为根据ultralytics版本的RT-DETR进行改进&#xff0c;内容持续更新&#xff0c;每周更新文章数量3-10篇。 专栏以ResNet18、ResNet50为基础修改版本&#xff0c;同时修改内容也支持Re…

Oracle 高级网络压缩 白皮书

英文版白皮书在这里 或 这里。 本文包括了对英文白皮书的翻译&#xff0c;和我觉得较重要的要点总结。 执行概述 Oracle Database 12 引入了一项新功能&#xff1a;高级网络压缩&#xff0c;作为高级压缩选项的一部分。 本文概述了高级网络压缩、其优点、配置细节和性能分析…