白菜通信过程调用框架
简介
仓库包含三个主要项目及其下属的各子项目
- 易语言实现的协程本体(基于Win32Fiber)与协程扩展(文件读写、TCP客户Duan)
- IPC(进程间通信,基于Win32API邮槽和共享内存实现,可采用APC/协程模型或IOCP/多线程模型)
- RPC(远程过程调用,易的实现模型同上,但关键在于跨语言能力,目前实现或已部分实现的语言有E、PHP、JS、Python)
github仓库:https://github.com/xbcsoft/BC-ProcedureCall
〇、序言·协程的五层境界
第一层:知道一个语言有所谓的执行流程让出机制,如轻量状态切换的yield跟生成器对象
第二层:结合异步IO的含事件消息循环的切换async/await,会用其封装的协程方法
第三层:基于底层封装提供的运行时协程API进行异步框架设计
第四层:理解操作系统线程的核心知识,运行时协程本体的重新设计或再优化(减少模拟栈或完整上下文的行为,并加上内存池、协程池或共享栈进一步减少开销和提升性能)
第五层:状态机协程(由编译器在全局统筹用较少的状态代价生成可复用可恢复的控制流代码)
一、易·协程本体篇(我在第三层)
前言
首先我必须得声明:我的协程实现基于Win32Fiber且用完即销毁(Fiber内存开销上硬性要求最低64KB保留栈(即Fiber最高上限在3w左右)和至少4KB提交栈,快是不可能有多快的,但我想比的压根不是开销跟性能)
我是知道这类运行时协程,后续可以通过内存池等手段来优化附加数据 亦或干脆直接将申请内存的地方放在Fiber实现的保留栈上(即做共享栈、搞Fiber池,而不是用完就销毁再新建)
甚至通过理解系统底层线程切换机制后自己另起炉灶从0开始实现更细致的运行时协程, 但这些实现起来肯定都很不优雅(由于涉及线程寄存器等上下文切换,纯易自身去写是不可能的,要么扣汇编要么封装DLL)
(后面我还会考察C++20的协程——相信在C++那边会得到最优解, 编译器层级的状态机协程,
而想扣汇编的话在编译期是不可能做到的、省去在易干脏活累活的事情了,
不过到时我也已经让易正式退出我的历史了)
在易这边属于最小化实现,代码逻辑清晰,真的不想写成屎山
此外我的目的仅是告诉易友们尽管非高性能下的协程它依然有存在的价值,以及让更多人知道基于协程底层API后的框架设计封装协程应用的基本原理。
- 单线程异步并发下可以写出整洁的同步化代码
- 单线程相比多线程天然无锁读写无冲突的优势,非常适合UI操作
- 协程扩展接口规范,怎样编写的很优雅方便扩展易的协程生态
协程源码里我内置了10000个4字节整型数组来静态存储各个hFbier,这意味着我对易协程的支持只能最大提交10000个任务(同时因Fiber开支要求会占用至少625 MB 的虚拟地址),但可后续通过等待机制阻止协程进一步提交创建新任务,即单线程中每隔100ms去轮询只有等可用的协程退出时才接着继续回到创建点继续创建协程(这部分看源码可知该妥协方案的妙处,【基础协程/! 协程压力测试.e】可测试超过1w的协程任务创建正确执行,当然这个方案也并非完美这个你可以仔细思考还会有什么不足)
等待对象信号(这里演示等待创建的进程退出)

三种方式的等待协程执行完毕
由于代码较多这里就不贴了(可详情源码"三种方式的等待协程执行完毕.e"),这里就简单聊聊
方法一:手动变量计数,手动切换协程
方法二:采用协程通道类(虽然也可以实现但并非协程通道的最佳应用场景)
方法三:自己封装变量计数(同时也将切换细节进行封装,推荐!)
探讨协程通道的真实使用场景
首先我的协程里实现的协程通道有两种模型(LIFO和FIFO,在无数据时 协程级阻塞,push是多生产者,但pop这里实现上局限于一个协程消费者)
其实对于模拟栈而言比模拟队列的意义更大(响应最新事件、覆盖型任务)
比如一种应用场景:适合仪表盘这种实时刷新数据的,现在有好几条数据一块到来了,那么以最后的数据显示为主(其余的可保留作为日志参考或直接丢弃)
而协程里纯队列FIFO(由于我这里实现的协程通常是单线程),那么这种FIFO我认为就没有太大的应用价值
原因是,能用上协程的地方多半是用上异步IO事件的,其给出的响应本身就是串行顺序处理,又或者数据还保留在内核的后续只不过需要赶上时才被接着消费(当然你说是为了快点从内核中摘除数据释放内核缓冲区,其实也不对,因为异步IO每次提交都是会创建分配新的用户内存空间且之后才给到协程上,所以这部分早就脱离内核缓冲区了——当然这部分是可以还再优化为使用先进的内存池管理了)
而这里FIFO本质就也只是普通顺序处理并没有重新做决策(还多了一层毫无必要的搬运)
故协程通道的队列版想要变得有价值,务必实现优先级队列才行(只有“可排序 / 可抢占”的事件队列才有价值)
这里我只是提一个idea,我自己是实际项目并还没用上这部分功能(而且优先级队列实现上也比传统的数据结构较为复杂,通常是完全二叉树实现的MinHeap)
子进程池下的协程服务(付费内容)
- 在各子进程中支持协程复用
- 一个子进程崩溃可自动重启不影响主进程
- 规避易的单个32位进程仅有2GB用户空间的限制
使用示例在【协程本体与协程生态/基础协程/4.子进程池版协程.e】
二、易·协程扩展篇
协程扩展空模板说明

通过当前进程的全局共享内存引出脱离原模块的协程原始接口函数(易的模块由于存在隔离冲突,更关键的是加上我的协程整体设计是单一主线程上的单例模式,其方法的调用务必要使用同一套函数);
PS:事实上其他语言引出虚函数接口还有更好的办法这里就不多提了,易这里已经是最优解,目前官方仅提供以下2个协程扩展案例(当前仅展示使用,封装设计参考仓库源码),方便后继者使用和学习我的协程生态。
1. 协程扩展-文件读写

2. 协程扩展-TCP客户Duan(基于重叠APC的纯异步单线程)
