今天的主角是一个分时调度协程的脚本语言——Melang。这篇文章主要介绍 Melang 的协程使用。关于 Melang 的安装,可以参考安装文档。
之所以强调分时调度,其实是为了强调 Melang 协程和其他语言协程的差异。在 Lua 、Go 中,当某一个协程运行时,如果不调用某些特定库函数或者 yield 之类的函数,那么当前协程将一直持有 CPU 执行权,也就导致了其他协程或者 I/O 事件或者其他类型事件无法被响应,进而导致整个系统出现运行异常的情况。诚然,经过精密的设计,可以完全避免这类问题。但也会给开发者增加维护和学习成本。而 Melang 的每个协程,都是运行一段时间后被解释器强制中断挂起,让其他协程或者 I/O 事件等得以运行和处理。
在 Melang 中,每一个协程就是一个脚本任务,它可能是文件,也可能只是一段代码片段。每一个协程都有相对独立的运行上下文(也就是相对独立的运行环境),但也允许通过脚本库函数使得多个协程间进行通信。
下面给出两种启用多个协程的方法:
//a.m
sys = Import('sys');
while (true) {
sys.print('a');
}
//b.m
sys = Import('sys');
while (true) {
sys.print('b');
}
假设我们有这两个协程示例(也是两个文件),它们的功能就是死循环向终端输出a
和b
。
我们可以通过如下命令在单个线程内运行这两个协程:
melang a.m b.m
可以看到他们的输出大致如下:
...
a
a
a
a
a
a
a
a
...
b
b
b
b
b
b
b
...
a
a
a
a
a
a
a
a
...
由于 lua 等老牌支持协程的语言实现缘故,可能一些读者会怀疑我在sys.print
中加入了强制切换协程的代码,那么可以使用如下示例自行验证一下。
//a.m
while (true){}
//b.m
while (true){}
//c.m
sys = Import('sys')
while (true) {
sys.print('c');
}
melang a.m b.m c.m
使用内置函数Eval
拉起协程。
我们首先假设同一路径下的有两个文件名为:a.m
和b.m
。当然,实际使用中,可以放在不同路径下,这里只是为了演示方便。
//a.m
sys = Import('sys');
Eval('b.m');
while (true) {
sys.print('a');
}
//b.m
sys = Import('sys');
while (true) {
sys.print('b');
}
然后执行如下命令:
melang a.m
我们可以得到与方法 1 中第一个示例一样的输出。
Eval
还有另一种用法,示例如下:
//a.m
sys = Import('sys');
Eval('sys = Import("sys"); while (true) {sys.print("b");}', nil, true);
while (true) {
sys.print('a');
}
然后执行
melang a.m
依旧可以得到一样的输出效果。
事实上,上述的两种方法都只是对脚本解释器提供的 API 的不同封装。我们也可以自己开发其他的协程启动形式。
感谢阅读!