« 正式开始前 | 返回首页 | 开发笔记 (1) »

Ameba , 一个简单的 lua 多线程实现

几个月以前,在我在 blog 上曾谈及 Lua 5.2 的改进。它可以用来实现抢占式多线程

周末休息,我把这桩事挖出来娱乐一下,花了一整个晚上做了实现。把 lua 的每个线程锁定在独立的 lua state 中,强迫线程之间通过消息管道的方式通讯。经过测试,Lua 5.2 每个独立的 state 占用的内存很小。通过自定义 alloc 函数可以测算出,一个干净的 32bit state ,不含任何库函数时,占用内存量在 2K 以下(1726 bytes)。如果加载基本库,也仅仅占用不到 4K (3265 bytes)。若把所有 lua 官方标准库加载进来,才会上升到 10K 以上(12456 bytes)。

对于 luajit 2 ,这个基础开销会大一些,最小开销也在 10K 左右 (8058 bytes) 。加上 ffi 达到 30k (31605 bytes)。不过 ffi 可以使 lua 代码直接使用 C 的数据结构,在实际运用中还可以减少内存的使用。

废话不多说,我的代码放在了 github 上 ,有兴趣的同学可自取。

这个娱乐项目命名为 Ameba ,暗示每个代码单位都足够的小,功能简单。它们必须通过很少的 send/recv 和外界通讯。目前,通讯的数据类型仅限于 number boolean 和 string 。

每个线程(ameba)可以调用 ameba 函数分裂一个新的 ameba 出去。调度器为每个 ameba 分配了一个唯一的通讯通道 (chan) 。ameba 自己可以通过自己的全局变量 chan 拿到这个数字。

读写 chan 可以通过 send / recv 两个 api 。如果返回值为 nil ,则表示 chan 已经被关闭。(目前只可能是 ameba 自己退出死亡)。chan 的读写是阻塞的。即,当你向 chan 写入数据(可以是一个或多个)时,若同时对端 ameba 没有在读取数据,那么写入者将被挂起;反之亦然。

recv 只可以收取系统为其分配的 chan 上的数据。send 则可以制定一个数字表示向哪个 ameba 发送信息。


关于实现:

抢先式多线程是用 lua 的 debug hook 实现的。它在一定程度上会降低 lua 代码执行的性能(不到一倍),可以通过修改时间片的粒度来调整。

每个 ameba 里都有一个 chan 读缓冲区,放在注册表中。当有另外的 ameba 企图写入的时候,首先写到这个缓冲区中,再由本地的 recv 函数复制出来。

整个系统的父 state 中保留有一个表,映射了所有 ameba 的读写队列。用于调度器的工作。这张表存在于父 state 的注册表中。

ameba 之间没有父子层级关系。

这个库是设计成可以递归使用的,可以在 ameba 内重新启动库,这样就可以有层级关系。但,chan id 是一个 C 里的全局变量,会一直累加。当然也可以修改实现,把这个变量放在 lua state 中。

目前的实现中,每个 ameba 的 state 只初始化了 lua 的基础库,没有加载完整的标准库。

自定义的 lua alloc 函数未来用于控制内存分配和管理,目前并没有特别使用。

整块代码娱乐性质为主,也就是写着玩儿。不保证质量。

代码必须装有 lua 5.2 beta 版才可能正常编译,经过少许修改也可以用于 luajit 2 beta 。但由于 lua 早期版本的实现限制,无法用于 lua 5.1 或更早的版本。

Comments

很巧妙的思路哈哈,有趣
博客的风格依然简约高效!
这是个玩具, 不要用它.
已经在你的github中提交了代码,请楼主尽快合并。
你好 风云大大 请问在lua5.2.3里检测版本用了更强的检测方式,那么在ameba.dll里已经连接过一次lua,然后再main里又链接了一次,所以我运行ameba的时候出现 mutleble vms 的错误,请问该如何解决呢。
请问一下, 可以把对应 luajit 2 的代码发出来吗?
好吧go in lua
第一次来的,学习学习
再次来过。 嘿嘿。 技术blog必须顶。
不太懂。 第一次来。 只知道多线程能提速。
不错,短小精悍,
我说怎么找不到加锁的代码,原来只在一个线程中实现的,看起来不错,非常巧妙,只不过好像实用价值不大。
基础开销会大一些
楼上的你golang都在学了怎么还不会翻墙啊。。。
不懂lua不懂lua< 老大还在玩go没有,golang的官网访问不到,老大有啥高招没啊

Post a comment

非这个主题相关的留言请到:留言本