Kotlin协程suspend的理解
suspend修饰符,它可以告诉编译器,该函数需要在协程中执行。作为开发者,您可以把挂起函数看作是普通函数,只不过它可能会在某些时刻挂起和恢复而已。协程的挂起就是退出方法,等到一定条件到来会重新执行这个方法,因为对方有这个协程的引用,当然可以调用你的方法了。
普通函数不可以调用挂起函数。因为挂起函数有有一个隐形的参数Continuation
,普通方法没有办法传Continuation
参数,Continuation
可以简单理解成java中的Callback
。
suspend修饰的方法不一定真的可以挂起,只要这个方法接收到COROUTINE_SUSPENDED
结果,就表示可以挂起了,即退出方法。
// 一个普通方法
private fun doFun1() {println("doFun1")
}// 一个suspend修饰的方法,但是方法内没有挂起点,方法体可普通方法一样,但是多一个Continuation参数
private suspend fun doSuspendFun1(p1: Int) {println("doSuspendFun1")
}// 方法中用了delay方法,delay方法返回了COROUTINE_SUSPENDED
// 表示doSuspendFun2方法在执行完delay方法后就会退出
private suspend fun doSuspendFun2(p1: Int, p2: Int) {delay(1000)println("doSuspendFun2")
}fun main() {GlobalScope.launch {println("launch")doSuspendFun2(1, 1)}
}
launch
方法的参数block
就是一个suspend CoroutineScope.() -> Unit
类型的方法。
我们看下转换成java后的代码
doFun1
方法没有什么变化
private static final void doFun1() {System.out.println("doFun1");
}
doSuspendFun1
方法不仅多了一个参数Continuation
,还多了一个返回值Object
类型,这就是为什么普通函数不能调用挂起函数,因为无法传递Continuation
参数。返回值是Object,因为可以返回COROUTINE_SUSPENDED
,也可以返回String,返回值就是协程的返回值,所以返回值的类型无法确定。因为内部没有使用挂起函数,内部的逻辑就和普通方法一样,就是多一个参数和一个返回结果。
private static final Object doSuspendFun1(int p1, Continuation $completion) {System.out.println("doSuspendFun1");return Unit.INSTANCE;
}
来看一下比较复杂的doSuspendFun2
方法,内部使用了delay
方法。就会有挂起点,所谓挂起点就是label
,很多文章叫这个为状态机
。执行到一个挂起点,label
的值就会加1,等下次恢复的时候,switch
就会知道从哪开始执行了。
private static final Object doSuspendFun2(int var0, int var1, Continuation var2) {// Continuation都是从上一个挂起函数中传进来的,并不是当前对象,所以才会重新创建new doSuspendFun2.1(var2)// 防止直接使用外面的的协程var2,影响到var2。doSuspendFun2.1 $continuation;label20: {// if (var2 instanceof doSuspendFun2.1) {$continuation = (doSuspendFun2.1)var2;if (($continuation.label & Integer.MIN_VALUE) != 0) {$continuation.label -= Integer.MIN_VALUE;break label20;}}// 就是下面的 Test14Kt$doSuspendFun2$1$continuation = new doSuspendFun2.1(var2);}Object $result = $continuation.result;// 它的值就是 COROUTINE_SUSPENDEDObject var5 = IntrinsicsKt.getCOROUTINE_SUSPENDED();switch ($continuation.label) {case 0:ResultKt.throwOnFailure($result);$continuation.getContext();// label加1$continuation.label = 1;if (DelayKt.delay(1000L, $continuation) == var5) {// delay返回 COROUTINE_SUSPENDED,所以直接退出方法return var5;}break;case 1:ResultKt.throwOnFailure($result);break;default:throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");}System.out.println("doSuspendFun2");// 没有返回值,默认就是 Unitreturn Unit.INSTANCE;}
协程中的Continuation
这样理解成java中的Callback
,应该简单了吧
挂起方法doSuspendFun2
最后被编译成一个对象,继承ContinuationImpl
final class Test14Kt$doSuspendFun2$1 extends ContinuationImpl {// $FF: synthetic fieldObject result;int label;Test14Kt$doSuspendFun2$1(Continuation $completion) {super($completion);}@Nullablepublic final Object invokeSuspend(@NotNull Object $result) {this.result = $result;this.label |= Integer.MIN_VALUE;return Test14Kt.access$doSuspendFun2(0, 0, (Continuation)this);}
}
suspend修饰的方法中如果使用了挂起函数,并且挂起函数可以返回COROUTINE_SUSPENDED
,jvm就会创建一个继承自ContinuationImpl
的类,这个类的invokeSuspend
方法就是在执行这个挂起方法(此时已经被转换成java的普通方法了)。
我们再看看main
函数里面的launch
处的方法。
final class Test14Kt$main$1 extends SuspendLambda implements Function2 {int label;Test14Kt$main$1(Continuation $completion) {super(2, $completion);}@Nullablepublic final Object invokeSuspend(@NotNull Object $result) {Object var2 = IntrinsicsKt.getCOROUTINE_SUSPENDED();switch (this.label) {case 0:ResultKt.throwOnFailure($result);System.out.println("launch");Continuation var10002 = (Continuation)this;this.label = 1;// 注意把当前对象传给 doSuspendFun2了,这就是Continuation再传递 if (Test14Kt.access$doSuspendFun2(1, 1, var10002) == var2) {// 如果doSuspendFun2方法返回COROUTINE_SUSPENDED,也会退出invokeSuspend方法return var2;}break;case 1:ResultKt.throwOnFailure($result);break;default:throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");}return Unit.INSTANCE;}@NotNullpublic final Continuation create(@Nullable Object value, @NotNull Continuation $completion) {return (Continuation)(new Test14Kt$main$1($completion));}@Nullablepublic final Object invoke(@NotNull CoroutineScope p1, @Nullable Continuation p2) {return ((Test14Kt$main$1)this.create(p1, p2)).invokeSuspend(Unit.INSTANCE);}
}
suspend CoroutineScope.() -> Unit
会被编译继承SuspendLambda
的一个类。
相关文章:
Kotlin协程suspend的理解
suspend修饰符,它可以告诉编译器,该函数需要在协程中执行。作为开发者,您可以把挂起函数看作是普通函数,只不过它可能会在某些时刻挂起和恢复而已。协程的挂起就是退出方法,等到一定条件到来会重新执行这个方法&#x…...

基于AI深度学习的中医针灸实训室腹针穴位智能辅助定位系统开发
在中医针灸的传统治疗中,穴位取穴的精确度对于治疗效果至关重要。然而,传统的定位方法,如体表标志法、骨度折量法和指寸法,由于观察角度、个体差异(如人体姿态和皮肤纹理)以及环境因素的干扰,往…...

51单片机教程(二)- 创建项目
1 创建项目 创建项目存储文件夹:C51Project 打开Keil5软件,选择 Project -> New uVision Project: 选择项目路径,即刚才创建的文件夹 选择芯片,选择 Microchip(微型集成电路)࿰…...
Rust 图形界面开发——使用 GTK 创建跨平台 GUI
第五章 图形界面开发 第一节 使用 GTK 创建跨平台 GUI GTK(GIMP Toolkit)是一个流行的开源跨平台图形用户界面库,适用于创建桌面应用程序。结合 Rust 的 gtk-rs 库,开发者能够高效地构建现代化 GUI 应用。本节将详细探讨 GTK 的…...

Hellinger Distance(赫林格距离)
Hellinger Distance(赫林格距离)是一种用于衡量两个概率分布相似度的距离度量。它通常用于概率统计、信息论和机器学习中,以评估两个分布之间的相似性。Hellinger距离的值介于0和1之间,其中0表示两个分布完全相同,1表示…...
【系统架构设计师】七、设计模式
7.1 设计模式概述 设计经验在实践者之间日益广泛地利用,描述这些共同问题和解决这些问题的方案就形成了所谓的模式。 7.1.1 设计模式的历史 建筑师Christopher Alexander首先提出了模式概念,他将模式分为了三个部分: 特定的情景ÿ…...

新工具可绕过 Google Chrome 的新 Cookie 加密系统
一位研究人员发布了一款工具,用于绕过 Google 新推出的 App-Bound 加密 cookie 盗窃防御措施并从 Chrome 网络浏览器中提取已保存的凭据。 这款工具名为“Chrome-App-Bound-Encryption-Decryption”,由网络安全研究员亚历山大哈格纳 (Alexander Hagenah…...

模型拆解(三):EGNet、FMFINet、MJRBM
文章目录 一、EGNet1.1编码器:VGG16的扩展网络 二、EMFINet2.1编码器:三分支并行卷积编码器2.2CFFM:级联特征融合模块2.3Edge Module:突出边缘提取模块2.4Bridge Module:桥接器2.5解码器:深度特征融合解码器…...
齐次线性微分方程的解的性质与结构
内容来源 常微分方程(第四版) (王高雄,周之铭,朱思铭,王寿松) 高等教育出版社 齐次线性微分方程定义 d n x d t n a 1 ( t ) d n − 1 x d t n − 1 ⋯ a n − 1 ( t ) d x d t a n ( t ) x 0 \frac{\mathrm{d}^nx}{\mathrm{d}t^n} a_1(t)\frac{\mathrm{d}^{n-1}x}{\math…...
Python-Celery-基础用法总结-安装-配置-启动
文章目录 1.安装 Celery2.配置 Celery3.启动 Worker4.调用任务5.任务装饰器选项6.任务状态7.定期任务8.高级特性9.监控和管理 Celery 是一个基于分布式消息传递的异步任务队列。它专注于实时操作,但也支持调度。Celery 可以与 Django, Flask, Pyramid 等 Web 框架集…...
vue中的nextTick() - 2024最新版前端秋招面试短期突击面试题【100道】
nextTick() - 2024最新版前端秋招面试短期突击面试题【100道】 🔄 在Vue.js中,nextTick 是一个重要的方法,用于在下次DOM更新循环结束之后执行回调函数。理解 nextTick 的原理和用法可以帮助你更好地处理DOM更新和异步操作。以下是关于 next…...
5G学习笔记三之物理层、数据链路层、RRC层协议
5G学习笔记三之物理层、数据链路层、RRC层协议 物理层位于无线接口协议栈的最底层,作用:提供了物理介质中比特流传输所需要的所有功能。 1.3.1 传输信道的类型 物理层为MAC层和更高层提供信息传输的服务,其中,物理层提供的服务…...
Ubuntu 通过Supervisor 或者 systemd 管理 .Net应用
在 Ubuntu 上安装 .NET 8.0,通过 supervisor 或 systemd 管理 .NET 应用服务,确保应用能够自动启动、运行以及在崩溃时重启。 1. 安装 .NET 8.0 最新的Ubuntu版本已经不需要注册 Microsoft 包存储库了,具体的可以参考微软官方文档安装&…...

超好用的视频剪辑软件分享:10款剪辑软件推荐
视频剪辑软件哪个比较好用?无论是短视频创作者、专业剪辑师,还是影视后期制作团队,选择一款合适的视频剪辑软件至关重要。今天,我将为大家分享几款超好用的视频剪辑软件,并介绍视频剪辑的六大核心流程。 1.影忆 特点&a…...
5G三大应用场景中的URLLC
5G三大应用场景中的URLLC 5G三大应用场景中的URLLC 1 Urllc不是一个独立的技术,更不是一张独立的网络,他是5G所谓的新空口标准NR(New Radio)中,涉及大规模降低时延、提高可靠性的相关技术; 2 Urllc在目前的…...

PyMOL中常用的命令列表
PyMOL中常用的命令列表 PyMOL中常用的命令列表,包括了加载文件、去除水分子、改变颜色、显示样式和图形优化等操作,可以帮助你完成全方位的分子展示设置。 基础命令流程 加载分子结构 load your_file.pdb # 加载PDB文件去除水分子 remove solvent …...
坏块处理 ORA-01578: ORACLE data block corrupted (file # 3, block # 152588)
帮客户检查环境时,发现sysaux表空间的数据文件有坏块,8月25日发生的,备份保留3个月,直接恢复处理。 rman备份log报错如下 RMAN-00571: RMAN-00569: ERROR MESSAGE STACK FOLLOWS RMAN-00571: RMAN-03009: failure of backu…...

像`npm i`作为`npm install`的简写一样,使用`pdm i`作为`pdm install`的简写
只需安装插件pdm-plugin-i即可: pdm plugin add pdm-plugin-i 然后就可以愉快地pdm i了,例如: git clone https://github.com/waketzheng/fast-dev-cli cd fast-dev-cli python -m pip install --user pipx pipx install pdm pdm plugin a…...

DNS域名解析服务器--RHCE
1.DNS简介 DNS ( Domain Name System )是互联网上的一项服务,它作为将域名和 IP 地址相互映射的一个分布式 数据库,能够使人更方便的访问互联网 DNS 系统使用的是网络的查询,那么自然需要有监听的 port 。 DNS 使用的是…...
数据库物化视图的工作原理与Java实现
引言 物化视图(Materialized View)是数据库中一种特殊的对象,它存储了查询结果的物理副本,使得复杂查询的结果可以快速地被访问。本文将详细介绍物化视图的工作原理、技术策略,并提供Java代码示例。 1. 物化视图的基…...
接IT方案编写(PPT/WORD)、业务架构设计、投标任务
1、IT 方案编写(PPT/WORD) 定制化方案:根据客户需求,提供涵盖云计算、大数据、人工智能等前沿技术领域的 PPT/WORD 方案编写服务,精准提炼核心价值,呈现专业技术内容。 逻辑清晰架构:采用…...

FineReport模板认证找不到模板
水善利万物而不争,处众人之所恶,故几于道💦 文章目录 1.现象及排查过程2. 解决办法 1.现象及排查过程 FR模板认证下面找不到模板 由于是集群部署的FR,所以后台查看了sftp服务器,测试连接,连接成功。 但是…...
东芝Toshiba DP-4528AG打印机信息
东芝 Toshiba DP 4528AG 是一款黑白激光数码复合机: 类型:激光数码复合机,涵盖复印、打印、扫描、传真功能,能满足办公室多样化的文档处理需求。速度类型:中速,黑白复印和打印速度可达 45 页 / 分钟&#…...
在 Kali 上打造渗透测试专用的 VSCode 环境
Kali Linux 是渗透测试领域的首选操作系统,搭配一款高效的代码编辑器可以显著提升工作效率。Visual Studio Code(VSCode)以其轻量、强大的扩展性和跨平台支持,成为许多安全研究者的选择。本文将详细介绍如何在 Kali Linux 上安装 …...
在 Caliper 中执行不同合约的方法
在 Caliper 中执行不同的智能合约需要通过正确配置工作负载(workload)和测试轮次(rounds),下面我将详细介绍如何执行不同的合约。 1. 通过 config.yaml 配置不同测试轮次 你可以在 config.yaml 中为不同的合约定义不同的测试轮次: rounds:- label: test-helloworlddescript…...

基于LLaMA-Factory和Easy Dataset的Qwen3微调实战:从数据准备到LoRA微调推理评估的全流程指南
随着开源大模型如 LLaMA、Qwen 和 Baichuan 的广泛应用,其基于通用数据的训练方式在特定下游任务和垂直领域中的表现仍存在提升空间,因此衍生出针对具体场景的微调训练需求。这些训练涵盖预训练(PT)、指令微调(SFT&…...
使用glide 同步获取图片
在 Glide 中,可以使用asBitmap()方法来获取图片的Bitmap对象,进而同步地加载图片。以下是具体示例: String imageUrl "https://example.com/image.jpg"; Bitmap bitmap Glide.with(context).asBitmap().load(imageUrl).apply(ne…...
12.6Swing控件4 JSplitPane JTabbedPane
JSplitPane JSplitPane 是 Java Swing 中用于创建分隔面板的组件,支持两个可调整大小组件的容器。它允许用户通过拖动分隔条来调整两个组件的相对大小,适合用于需要动态调整视图比例的场景。 常用方法: setLeftComponent(Component comp)&a…...

实践指南:从零开始搭建RAG驱动的智能问答系统
LLM 赋能的最强大的应用之一是复杂的问答 (Q&A) 聊天机器人。这些是可以回答关于特定来源信息问题的应用程序。这些应用程序使用一种称为检索增强生成的技术,或 RAG。本文将展示如何基于 LangChain 构建一个简单的基于非结构化数据文本数据源的问答应用程序。 温…...
登高架设作业操作证考试:理论题库高频考点有哪些?
一、安全基础知识 法律法规 《安全生产法》《特种作业人员安全技术培训考核管理规定》中关于登高作业的强制性要求(如持证上岗、培训时限等)。 事故责任划分:未系安全带、无监护作业等违规行为的法律后果。 个人防护 安全带使用标准&#…...