当前位置: 首页 > news >正文

【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())}}
}
http://www.lryc.cn/news/459863.html

相关文章:

  • java计算机毕设课设—扫雷游戏(附源码、文章、相关截图、部署视频)
  • AndroidLogger 使用问题
  • 数据库常见面试
  • boxplot 绘制箱线图,添加数据点
  • 用sdkman管理多个jdk切换
  • 【AIGC】ChatGPT提示词Prompt高效编写模式:结构化Prompt、提示词生成器与单样本/少样本提示
  • 反调式实战(有道翻译窗口弹出)
  • verilog端口使用注意事项
  • Docker常用命令大全汇总
  • LVS-DR+Keepalived 高可用群集部署
  • 【elasticsearch】安装和启动
  • Golang 逃逸分析(Escape Analysis)理解与实践篇
  • React入门 9:React Router
  • MATLAB基础应用精讲-【数模应用】Bland-Altman图(附python和R语言代码实现)
  • ARM/Linux嵌入式面经(四一):中兴面经
  • 鸿蒙虚拟运行环境
  • SpringCloud-Consul
  • nginx搭建负载均衡
  • 灵当CRM data/pdf.php 任意文件读取漏洞复现
  • Python 批量转换 Shapefile 为 GeoJSON
  • 软考《信息系统运行管理员》- 4.1信息系统软件运维概述
  • Leetcode 3319. K-th Largest Perfect Subtree Size in Binary Tree
  • 从秒级到小时级:TikTok等发布首篇面向长视频理解的多模态大语言模型全面综述
  • 【CTF】敏感信息泄露 GIT SVN VIM
  • EMQX服务器的搭建,实现本地机和虚拟机之间的MQTT通信(详细教程)
  • cordova的使用
  • 三种Transformer模型中的注意力机制介绍及Pytorch实现:从自注意力到因果自注意力
  • 《使用Gin框架构建分布式应用》阅读笔记:p20-p31
  • 如何修改MacOS的终端的配色和linux一样
  • 基于百度智能体开发爱情三十六计