【Coroutines】Implement Lua Coroutine by Kotlin - 2
Last Chapter Link
文章目录
- Symmetric Coroutines
- Non-Symmetric Coroutine Sample
- Symmetric Coroutine Sample
- How to Implement Symmetric Coroutines
- Wonderful Tricks
- Code Design
- Tail Recursion Optimization
- Full Sources
Symmetric Coroutines
in last blog, we have talked about how to implement lua-style coroutine
there are two kinds of coroutines, symmetric and non-symmetric
-
symmetric
when coroutine suspend or complete, execution go back to the point resume it
that means coroutines have a hierarchical relation of calling
-
non-symmetric
each coroutine is independent, coroutine can specify where to go when suspend
Non-Symmetric Coroutine Sample
like this, implemented in last blog
package x.coroutinesuspend fun main() {val producer = GlobalScope.launch<Unit, Int>(Dispatchers.new()) {for (i in 1..3)yield(i)return@launch 0}val consumer = GlobalScope.launch<Int, Unit>(Dispatchers.new()) {for (i in 1..3)yield(Unit)return@launch Unit}while (!producer.completed() && !consumer.completed()) {val param1 = producer.resume(Unit)val param2 = consumer.resume(param1)}
}
Symmetric Coroutine Sample
which we will talk about soon in this blog
package x.coroutinesuspend fun main() {lateinit var coroutine1: SymmetricCoroutine<String>lateinit var coroutine2: SymmetricCoroutine<String>lateinit var coroutine3: SymmetricCoroutine<String>coroutine1 = createSymmetric {println("parameter ${getParameter()}")transfer(coroutine3, "d")}coroutine2 = createSymmetric {transfer(coroutine1, "c")}coroutine3 = createSymmetric {println("symmetric start")transfer(coroutine2, "b")transfer(coroutine1, "e")}val main = launchSymmetric(coroutine3, "a")coroutine1.clean()coroutine2.clean()coroutine3.clean()println("symmetric end")
}
each coroutine can randomly goto another coroutine, with a input param carried
How to Implement Symmetric Coroutines
kotlin built-in coroutine is the non-symmetric one
but we can implement symmetric coroutines through non-symmetric ones
obviously transfer
is the core api that we need to implement
transfer
suspend current coroutine, and resume another coroutine, with a yielded param carried
this point is same to non-symmetric coroutines
the difference is, symmetric coroutine
will never go back to previous coroutine
Wonderful Tricks
if we create a implicit main coroutine
when coroutine a
want to transfer to coroutine b
it can deliver coroutine b
and resume parameter
to main coroutine
then let the main coroutine
resume coroutine b
that is, a suspend, return back to main, then main resume b
now, it is totally same with the non-symmetric coroutines
the yield result is target coroutine + resume param
Code Design
SymmetricCoroutine
hold a Coroutine
object, that responsible for execution schedule
when main coroutine
calls transfer
, it will resume work coroutine
and wait for its result
when work coroutine
calls transfer
, it will yield a TransferContext
object as result, then resume main coroutine
TransferContext
is composed of next coroutine
object and a coroutine resume parameter
when main coroutine
received the TransferContext
as a result, it will transfer the next coroutine
again
internal val coroutine: CoroutineImpl<T, TransferContext<*>?> = CoroutineImpl(context) {block()return@CoroutineImpl null
}
data class TransferContext<T>(val coroutine: SymmetricCoroutine<T>,val parameter: T?
)
private tailrec suspend fun <R> transferInner(other: SymmetricCoroutine<R>, param: Any?) {if (!isMain) {val transferContext = TransferContext(other, param as R)coroutine.yield(transferContext)return}if (!other.isMain()) {val impl = other as SymmetricCoroutineImpl<R>val transferContext = impl.coroutine.resume(param as R)transferContext?.let {transferInner(it.coroutine, it.parameter)}}
}
these are core codes, while the remains are auxiliary, just to fulfill details and offer easy-to-use apis
Tail Recursion Optimization
we notice that, all transfer work in work coroutines
are actually implemented by recursive execution of MainCoroutine.transfer
, until all work coroutines finished
when work coroutines works for a long time, calling stack of main coroutine will become bigger and bigger
eventually caused StackOverflowError
error
kotlin offers a tailrec
keyword to optimize recursive execution
the theroy of tailrec
is, use while instead of recursion, to avoid stack size increase
Full Sources
package x.coroutinesuspend fun main() {lateinit var coroutine1: SymmetricCoroutine<String>lateinit var coroutine2: SymmetricCoroutine<String>lateinit var coroutine3: SymmetricCoroutine<String>coroutine1 = createSymmetric {println("parameter ${getParameter()}")transfer(coroutine3, "d")}coroutine2 = createSymmetric {transfer(coroutine1, "c")}coroutine3 = createSymmetric {println("symmetric start")transfer(coroutine2, "b")transfer(coroutine1, "e")}val main = launchSymmetric(coroutine3, "a")coroutine1.clean()coroutine2.clean()coroutine3.clean()println("symmetric end")
}
package x.coroutineimport kotlin.coroutines.EmptyCoroutineContextinterface SymmetricCoroutine<T> {fun isMain(): Booleansuspend fun clean()
}interface SymmetricCoroutineScope<T> {fun getParameter(): Tsuspend fun <R> transfer(other: SymmetricCoroutine<R>, param: R)
}data class TransferContext<T>(val coroutine: SymmetricCoroutine<T>,val parameter: T?
)fun <T> createSymmetric(block: suspend SymmetricCoroutineScope<T>.() -> Unit
): SymmetricCoroutine<T> {return SymmetricCoroutineImpl(EmptyCoroutineContext, block)
}suspend fun <T> launchSymmetric(symmetric: SymmetricCoroutine<T>, param: T
): SymmetricCoroutine<Unit> {val main = SymmetricCoroutineImpl<Unit>(EmptyCoroutineContext) {transfer(symmetric, param)}main.isMain = truemain.coroutine.resume(Unit)return main
}
package x.coroutineimport kotlin.coroutines.CoroutineContextinternal class SymmetricCoroutineImpl<T>(context: CoroutineContext,block: suspend SymmetricCoroutineScope<T>.() -> Unit
) : SymmetricCoroutine<T>, SymmetricCoroutineScope<T> {internal var isMain = falseinternal val coroutine: CoroutineImpl<T, TransferContext<*>?> = CoroutineImpl(context) {block()return@CoroutineImpl null}override fun isMain() = isMainoverride fun getParameter(): T {return coroutine.parameter!!}override suspend fun <R> transfer(other: SymmetricCoroutine<R>, param: R) = transferInner(other, param)private tailrec suspend fun <R> transferInner(other: SymmetricCoroutine<R>, param: Any?) {if (!isMain) {val transferContext = TransferContext(other, param as R)coroutine.yield(transferContext)return}if (!other.isMain()) {val impl = other as SymmetricCoroutineImpl<R>val transferContext = impl.coroutine.resume(param as R)transferContext?.let {transferInner(it.coroutine, it.parameter)}}}override suspend fun clean() {while (!coroutine.completed()) {coroutine.resume(getParameter())}}
}
相关文章:
【Coroutines】Implement Lua Coroutine by Kotlin - 2
Last Chapter Link 文章目录 Symmetric CoroutinesNon-Symmetric Coroutine SampleSymmetric Coroutine SampleHow to Implement Symmetric CoroutinesWonderful TricksCode DesignTail Recursion OptimizationFull Sources Symmetric Coroutines in last blog, we have talk…...

java计算机毕设课设—扫雷游戏(附源码、文章、相关截图、部署视频)
这是什么系统? 资源获取方式再最下方(本次10月份活动福利,免费提供下载,自行到对应的方式1下载,csdn的0积分下载) java计算机毕设课设—扫雷游戏(附源码、文章、相关截图、部署视频) 基于Java的扫雷游戏…...

AndroidLogger 使用问题
Q1:解压zip后,启动Notepad未看到AndroidLogger工具栏 请检查plugins下安装位置是否正确,必须与下图一致,再确认Notepad 是否为 x64 ? Q2:使用 adb 可以显示已连接,但是获取不到日志 暂时不确定问…...

数据库常见面试
8道面试题 目录 目录 7道面试题 1.怎样进行sql优化 4、group by优化 5、limit优化 6、count优化 7、update优化 2.。怎样查看sql执行情况呢(哪个关键字),说说你对这个关键字的认识 4) possible_key: 5) key 3.说说你对innodb和 myisam的理解 …...

boxplot 绘制箱线图,添加数据点
先看效果图 import matplotlib.pyplot as plt #! 解决不显示的问题:中文设置为宋体格式 plt.rcParams[font.family] ["Times New Roman", SimSun]def plot_boxplot(data_list, out_file, x_custom_labels):# 画图fig, ax plt.subplots(figsize(90, 6…...

用sdkman管理多个jdk切换
前言 最近项目前后端进行升级,需要在jdk8和jdk17两个版本切换。最简单的是通过手动切换,但切换过程太繁琐,修改环境变量,达到切换目的。于是尝试其它解决方案,最终确实使用sdkman工具。 sdkman 是一款面向Java开发者的…...

【AIGC】ChatGPT提示词Prompt高效编写模式:结构化Prompt、提示词生成器与单样本/少样本提示
💯前言 在如今AI技术迅猛发展的背景下,尽管像ChatGPT这样的大型语言模型具备强大的生成能力,但它们的输出质量有时仍难以完全满足我们的预期。为了让ChatGPT生成更加准确、可靠的内容,掌握高效的Prompt编写技巧变得尤为重要。本文…...

反调式实战(有道翻译窗口弹出)
1.添加脚本断点实现源码获取 2.Function构造器构造debugger 因为是窗口被弹出的情况,所以window.closefunction()构造debugger。 3.定位到影响弹出的JavaScript代码片段 反调试思想:置空和替换,所以将其JavaScript进行注释或者删除。 这里主…...

verilog端口使用注意事项
下图存在组合逻辑反馈环,即组合逻辑的输出反馈到输入(赋值的左右2边存在相同的信号),此种情况会造成系统不稳定。比如在data_in20的情况下,在data_out0 时候,输出的数据会反馈到输入,输入再输出,从而造成不…...
Docker常用命令大全汇总
Docker是一种流行的容器化平台,可以在一个独立的、隔离的环境中构建、部署和运行应用程序。了解Docker常用命令可以帮助我们更高效地管理容器,快速开发和部署应用。本文将整理一系列Docker的常用命令,便于日常使用和学习。 1 Docker基础命令 1.1 启动/停止/重启docker # …...

LVS-DR+Keepalived 高可用群集部署
LVS-DRKeepalived 高可用群集部署 Keepalived 的工作原理LVSKeepalived 高可用群集部署配置负载调度器(主、备相同)关闭防火墙和核心防护及准备IPVS模块配置keeplived(主、备DR 服务器上都要设置)启动 ipvsadm 服务调整 proc 响应…...
【elasticsearch】安装和启动
启动 Elasticsearch 并验证其是否成功运行通常涉及以下步骤: 下载和安装 Elasticsearch: 访问 Elasticsearch 官方网站下载页面:https://www.elastic.co/guide/en/elasticsearch/reference/current/install-elasticsearch.html根据你的操作系…...
Golang 逃逸分析(Escape Analysis)理解与实践篇
Golang 逃逸分析(Escape Analysis)理解与实践篇 文章目录 1.逃逸分析2.相关知识(栈、堆、GC分析)3.逃逸分析综合-实践 demo 逃逸分析(Escape Analysis)是编译器在编译期进行的一项优化技术,是Gl…...

React入门 9:React Router
1. 什么是路由 路由(routing)就是通过互联的网络把信息从源地址传输到目的地址的活动。 以上是中文维基百科对路由的解释。通俗的来讲,把一个地方的信息传输到他想去的目的地的过程,就叫路由。 2. 用代码解释路由 需求:…...
MATLAB基础应用精讲-【数模应用】Bland-Altman图(附python和R语言代码实现)
目录 前言 几个高频面试题目 Bland-altman图:如何改变y轴 算法原理 Bland-Altman一致性分析 一致性界限 1. 背景介绍 2. Bland-Altman 法 3. batplot 命令介绍 4. 应用实例 Prism GraphPad实现Bland-Altman图 1.输入数据 2.从数据表中选择Bland-Altman分析 3.检…...
ARM/Linux嵌入式面经(四一):中兴面经
1. 请介绍一下您在嵌入式系统开发中的项目经验。 在嵌入式系统开发领域,我积累了丰富的项目经验,这些经验不仅锻炼了我的技术能力,也让我对嵌入式系统的设计和实现有了更深入的理解。以下是我参与的一个具有代表性的嵌入式系统开发项目的详细介绍: 项目背景 该项目是为一…...

鸿蒙虚拟运行环境
加一个环境变量:%SystemRoot%\System32\Wbem pushd "%~dp0" dir /b %SystemRoot%\servicing\Packages\*Hyper-V*.mum >hyper-v.txt for /f %%i in (findstr /i . hyper-v.txt 2^>nul) do dism /online /norestart /add-package:"%SystemRoot%…...

SpringCloud-Consul
为什么引入 Consul 简介以及安装 控制台 localhost:8500 服务注册与发现 服务端 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-consul-discovery</artifactId><exclusions><exclusio…...
nginx搭建负载均衡
准备工作 两台虚拟机,或者本地启动两个相同应用,在不同的端口上安装好的nginx,在linux上两个版本的hexo,或者其他应用,方便观察是否进行了负载均衡 启动服务 在两台虚拟机上启动项目,这里以hexo为例 服务器…...

灵当CRM data/pdf.php 任意文件读取漏洞复现
0x01 产品简介 灵当CRM是一款专为中小企业打造的智能客户关系管理工具,由上海灵当信息科技有限公司开发并运营。广泛应用于金融、教育、医疗、IT服务、房地产等多个行业领域,帮助企业实现客户个性化管理需求,提升企业竞争力。无论是新客户开拓、老客户维护,还是销售过程管…...

springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误
HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误,它们的含义、原因和解决方法都有显著区别。以下是详细对比: 1. HTTP 406 (Not Acceptable) 含义: 客户端请求的内容类型与服务器支持的内容类型不匹…...

2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...
Spring Boot面试题精选汇总
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...
C#中的CLR属性、依赖属性与附加属性
CLR属性的主要特征 封装性: 隐藏字段的实现细节 提供对字段的受控访问 访问控制: 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性: 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑: 可以…...

解析奥地利 XARION激光超声检测系统:无膜光学麦克风 + 无耦合剂的技术协同优势及多元应用
在工业制造领域,无损检测(NDT)的精度与效率直接影响产品质量与生产安全。奥地利 XARION开发的激光超声精密检测系统,以非接触式光学麦克风技术为核心,打破传统检测瓶颈,为半导体、航空航天、汽车制造等行业提供了高灵敏…...
6个月Python学习计划 Day 16 - 面向对象编程(OOP)基础
第三周 Day 3 🎯 今日目标 理解类(class)和对象(object)的关系学会定义类的属性、方法和构造函数(init)掌握对象的创建与使用初识封装、继承和多态的基本概念(预告) &a…...
接口 RESTful 中的超媒体:REST 架构的灵魂驱动
在 RESTful 架构中,** 超媒体(Hypermedia)** 是一个核心概念,它体现了 REST 的 “表述性状态转移(Representational State Transfer)” 的本质,也是区分 “真 RESTful API” 与 “伪 RESTful AP…...

【多线程初阶】单例模式 指令重排序问题
文章目录 1.单例模式1)饿汉模式2)懒汉模式①.单线程版本②.多线程版本 2.分析单例模式里的线程安全问题1)饿汉模式2)懒汉模式懒汉模式是如何出现线程安全问题的 3.解决问题进一步优化加锁导致的执行效率优化预防内存可见性问题 4.解决指令重排序问题 1.单例模式 单例模式确保某…...