【Coroutines】Implement Lua Coroutine by Kotlin - 2

news2024/12/21 20:09:21

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.coroutine

suspend 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.coroutine

suspend 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.coroutine

suspend 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.coroutine

import kotlin.coroutines.EmptyCoroutineContext

interface SymmetricCoroutine<T> {

    fun isMain(): Boolean

    suspend fun clean()
}

interface SymmetricCoroutineScope<T> {

    fun getParameter(): T

    suspend 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 = true
    main.coroutine.resume(Unit)
    return main
}
package x.coroutine

import kotlin.coroutines.CoroutineContext

internal class SymmetricCoroutineImpl<T>(
    context: CoroutineContext,
    block: suspend SymmetricCoroutineScope<T>.() -> Unit
) : SymmetricCoroutine<T>, SymmetricCoroutineScope<T> {

    internal var isMain = false

    internal val coroutine: CoroutineImpl<T, TransferContext<*>?> = CoroutineImpl(context) {
        block()
        return@CoroutineImpl null
    }

    override fun isMain() = isMain

    override 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.coloradmin.cn/o/2211327.html

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

相关文章

优先算法1--双指针

“一念既出&#xff0c;万山无阻。”加油陌生人&#xff01; 目录 1.双指针--移动零 2.双指针-复写零 ok&#xff0c;首先在学习之前&#xff0c;为了方便大家后面的学习&#xff0c;我们这里需要补充一个知识点&#xff0c;我这里所谓的指针&#xff0c;不是之前学习的带有…

RISC-V笔记——语法依赖

1. 前言 Memory consistency model定义了使用Shared memory(共享内存)执行多线程(Multithread)程序所允许的行为规范。RISC-V使用的内存模型是RVWMO(RISC-V Weak Memory Ordering)&#xff0c;该模型旨在为架构师提供更高的灵活性&#xff0c;以构建高性能可拓展的设计&#x…

51单片机的土壤湿度检测控制系统【proteus仿真+程序+报告+原理图+演示视频】

1、主要功能 该系统由AT89C51/STC89C52单片机LCD1602显示模块土壤湿度传感器继电器按键、蜂鸣器、LED等模块构成。适用于智能浇花浇水系统、花卉灌溉系统等相似项目。 可实现功能: 1、LCD1602实时显示土壤湿度 2、土壤湿度传感器&#xff08;滑动变阻器模拟&#xff09;采集…

鸿蒙--商品列表

这里主要利用的是 List 组件 相关概念 Scroll:可滚动的容器组件,当子组件的布局尺寸超过父组件的视口时,内容可以滚动。List:列表包

gitlab保护分支设置

版本&#xff1a;gitlab10.2.2 一旦设置master分支被保护&#xff0c;除了管理员之外的任何用户都无法直接向master提交代码&#xff0c;只要提交代码就会报错 # git push -u origin master Total 0 (delta 0), reused 0 (delta 0) remote: GitLab: You are not allowed to pu…

前端优化,解决页面加载慢

问题&#xff1a;vue项目使用vite打包后&#xff0c;部署在nginx服务器上&#xff0c;页面上访问时很慢&#xff0c;发现有个js文件很大导致加载很慢 先说结论&#xff1a; 方式时间未优化前21s开启压缩&#xff08;6级&#xff09;6s去掉大依赖&#xff08;flowable&#xf…

【CTF-SHOW】 web入门 web11-域名隐藏信息 【详-域名】

这道题的主要思路是通过DNS查询&#xff08;或利用题目中所给的网址直接查询&#xff09;指定域名解析以获得txt记录 1.什么是域名&#xff1f; 域名&#xff08;Domain Name&#xff09; 是互联网上用来标识网站或网络服务的名字&#xff0c;它是一个人类易于记忆和使用的地…

InfluxDB快速掌握

文章目录 1、InfluxDB简介2、InfluxDB数据结构3、InfluxDB存储架构4、InfluxDB基本操作1_数据库操作2_数据表操作3_数据保存策略4_数据查询操作 5、存储引擎6、总结 1、InfluxDB简介 时序数据库是近几年一个特殊的概念&#xff0c;与传统的Mysql关系型数据库相比&#xff0c;它…

算法:560.和为k的子数组

题目 链接:leetcode链接 思路分析&#xff08;前缀和&#xff09; 注意&#xff1a;我们前面讲过滑动窗口可以处理子数组、子串等问题&#xff0c; 但是在这道题目里面注意数据范围 -1000 < nums[i] < 1000 nums[i]可正可负&#xff0c;区间的和没有单调性&#xff0c;使…

Python案例 |地图绘制及分级着色

1、分级着色地图 分级着色地图常用于可视化地理数据&#xff0c;比如人口密度、经济数据、气候变化等。其原理是使用颜色或阴影的渐变来表示不同区域(如国家、省份、城市等)中的数据差异。例如&#xff0c;地图上的每个区域根据其代表的数值被着色&#xff0c;通常数值越大&am…

React Leaflet + React Pixi:双倍的快乐,我全都要

一篇实用性的文章&#xff0c;记录一下最近在自娱自乐使用 Leaflet 和 PixiJS 的过程中整的一个有意思的活&#xff0c;帮助我们使用 React 声明式的语法在 Leaflet 的图层上使用 PixiJS 绘图。 如果你对这些库和它们的用途都已有所了解&#xff0c;只想直接看代码的话&#xf…

今日指数项目day8实战权限管理功能(下)

3.4 权限添加按钮 1&#xff09;原型效果 2&#xff09;接口说明 功能描述&#xff1a; 权限添加按钮 服务路径&#xff1a; /api/permission 服务方法&#xff1a;Post请求参数格式: {"type":"1", //菜单等级 0 顶级目录 1.目录 2 菜单 3 按钮"t…

牛筋面,一口就爱上的神仙美食

宝子们&#x1f44b;&#xff0c;今天我一定要给大家种草一款超级好吃的美食 —— 食家巷牛筋面&#x1f60b;。&#x1f380;牛筋面真的是一种神奇的存在✨。它的口感 Q 弹有嚼劲&#xff0c;就像在你的嘴巴里跳舞一样&#x1f483;。每一根面条都裹满了浓郁的酱汁&#xff0c…

原生mybatis框架引入mybatisplus,调用接口时找不到原生Mapper自带的默认方法

1.yaml配置文件和Mapper注解基本上可以不用关注&#xff0c;因为mybatis能用就证明这俩多半是没有问题的 2.再看看是不是映射出了问题&#xff0c;像Namespace的空间包名和Mapper没有对上之类的 这个框架的问题是出在配置mybatis的config文件当中&#xff0c;原本的config文件用…

Unity 从零开始搭建一套简单易用的UGUI小框架 扩展与优化篇(完结)

一个通用的UGUI小框架就算是写完了&#xff0c;下面是一步步的思考与优化过程 Unity 从零开始搭建一套简单易用的UGUI小框架 基础分析篇-CSDN博客 Unity 从零开始搭建一套简单易用的UGUI小框架 功能撰写与优化篇-CSDN博客 从使用者的角度来整理一下可能会发出的疑问 0. Panel…

【微服务】springboot远程docker进行debug调试使用详解

目录 一、前言 二、线上问题常用解决方案 2.1 微服务线上运行中常见的问题 2.2 微服务线上问题解决方案 2.3 远程debug概述 2.3.1 远程debug原理 2.3.2 远程debug优势 三、实验环境准备 3.1 搭建springboot工程 3.1.1 工程结构 3.1.2 引入基础依赖 3.1.3 添加配置文…

YOLO11改进 | 注意力机制| 对小目标友好的BiFormer【CVPR2023】

秋招面试专栏推荐 &#xff1a;深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转 &#x1f4a1;&#x1f4a1;&#x1f4a1;本专栏所有程序均经过测试&#xff0c;可成功执行&#x1f4a1;&#x1f4a1;&#x1f4a1; 本文介绍了一种新颖的动态稀疏注意力机制…

C++,STL 029(24.10.13)

内容 一道练习题。 &#xff08;涉及string&#xff0c;vector&#xff0c;deque&#xff0c;sort&#xff09; 题目&#xff08;大致&#xff09; 有五名选手ABCDE&#xff0c;10个评委分别对每一个选手打分&#xff0c;去除最高分和最低分&#xff0c;取平均分。 思路&…

bat脚本banenr

飞出个未来班得 echo off echo .-. echo ( ) echo - echo J L echo ^| ^| echo J L echo ^| ^| echo J L echo …

Node.js概述

1. Node.js简介 Node.js是一个基于Chrome V8引擎的JavaScript运行环境。 地址&#xff1a;Node.js 中文网 1.1 Node.js中的JavaScript运行环境 &#xff08;1&#xff09;浏览器是JavaScript的前端运行环境 &#xff08;2&#xff09;Node.js是JavaScript的后端运行环境 …