抢占式协程调度新语言

转载自本人今日头条:https://www.toutiao.com/i6915650155899453966/
转载请注明出处

协程这个概念想必大家现在已经不会太过陌生了,毕竟不管是LUA还是GO抑或Kotlin甚至C++都有了协程。

虽然协程已经广泛出现了,但是各家的实现还是有所不同的。例如LUA,它的协程属于主动让出式的调度方式,即如果不主动让出执行权限,当前协程会一直执行而不会让其他协程执行。而有些语言的协程还是可以跨线程的运行的,可以说是实现方式五花八门。

今天给大家介绍的一款新语言就是一款按时间片抢占式调度的协程脚本语言,即每一个协程不需要显示调用某些特定函数来让出控制权,而是按照执行时间的权重来控制是否挂起还是运行。

这就是我们今天的主题——Melang (官方站点 https://melang.org/)
抢占式协程调度新语言

抢占式协程调度新语言
我们将讨论如下几方面内容:

  • 语言特性
  • 社区特性
  • 使用场景
  • 语言特性

语言特性

对于一款语言,它都注定会有循环、判断、数组、结构等等必要组成部分,这些Melang与其他语言并无二致,读者可以在Melang的官方站点找到相关文档。所以我们的重点就放在了以下方面上:

1.协程抢占式调度

这是这个语言最大的特点。在Melang中,每一个脚本任务都是一个独立的协程,协程与协程间是环境隔离的,如果需要通信,则需要使用消息函数进行通信。

我们看两个例子:

利用melang可执行程序启动多个脚本任务

假设我有如下两个脚本文件:

//a.mln
while (true) {
  @mln_print('a');
}
//b.mln
while (true) {
  @mln_print('b');
}

我执行如下命令:

# melang a.mln b.mln

执行后,会看到屏幕交替打印a和b,如下:

...
b
b
b
a
a
a
a
a
...

且使用top命令会发现只有一个进程(同时也只有一个线程)在执行这两个任务。

melang的调度并非发生在函数mln_print中,而是在处理每条语句的过程中都会尝试切换

在melang任务中拉起多个脚本任务

我们保留上一例子中的两个代码文件,并增加如下代码文件:

//launcher.mln
@mln_eval('a.mln');
@mln_eval('b.mln');

然后执行:

# melang launcher.mln

我们将会看到和上一例相同的输出结果,只是这一次,a和b两个任务是由另一个脚本任务通过函数mln_eval拉起的。

2.反射

反射用网友的一句话来总结就是:

在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。

在Melang中,类的概念称作集合(Set),不过更类似于C中的结构体,但还支持函数定义。

我们看一个简单的例子:

Human {
  name;
  age;
  @init (name, age) {
    this.name = name;
    this.age = age;
  }
}

这里我们定义了一个名为Human的集合结构,它有两个成员变量——name和age,以及一个方法——init。

常规实例化代码如下:

me = $Human;

这样就实例化了一个Human对象,名为me。

然而我们也可以如此实例化:

str = 'Human';
me = $str;

这个效果是一样的。

然后我们就可以调用对象的方法了:

me.init('Tom', 18);

在Melang中,不仅集合结构可以通过字符串来获取,函数以及对象方法都可以如此使用。

函数的例子

@foo ()
{
  @mln_print('foo');
}
a = 'foo';
a();

这里我们定义了一个函数,但是我们并不直接调用它,而是通过一个字符串类型变量来间接调用。

对象方法的例子

Human {
  name;
  age;
  action;
  @init (name, age) {
    this.name = name;
    this.age = age;
  }
}

me = $Human;
me.action = 'init';
me.action('Tom', 18);
@mln_print(me.name);

可以看到,本例中,我们通过成员变量中的字符串来调用对象的方法。

3.注入

对于一个已经实例化的对象,我们依旧可以对其成员和方法进行增加(或者说注入),例如:

Human {
}
someone = $Human;
someone.age = 20;
@mln_print(someone.age);

这段代码会输出20。说明age成员被成功加入someone对象。

注意:注入是针对对象的,而不是集合结构定义的。因此如果再对Human进行实例化,依旧没有age成员的。

上面这个例子是一种最简单的注入,Melang还提供了类似其他脚本的set和get函数。

我们再来看一个注入方法的例子:

Human {
  age;
}
@printAge()
{
  @mln_print(this.age);
}
someone = $Human;
someone.age = 20;
someone.print = printAge;
someone.print();

这个例子依旧会输出20,只是这次是由注入的方法调用mln_print进行输出的。

4.响应式编程

这一特性是借鉴于一些web前端开发的内容,是利用一组函数对变量进行监控,当值发生改变时,会调用设置好的回调函数进行处理。(注意,即便新赋予的值与旧值相同也会被调用)

示例:

@eventHandler(newdata, userData)
{
  @mln_print(newdata);
  @mln_print(userData);
}
n = 0;
@mln_watch(n, eventHandler, 'this is an useData');
for (; n < 5; ++n)
  ;

这里,利用mln_watch函数对变量n进行跟踪,当n的值改变时,就会触发eventHandler函数进行调用。我们将看到如下输出:

1
this is an useData
2
this is an useData
3
this is an useData
4
this is an useData
5
this is an useData

5.同步代码异步执行

Melang的代码在写法上都是同步的形式。同步形式的好处就是逻辑连贯,不会像异步处理那样逻辑跳来跳去。在传统语言中,同步写法也意味着同步处理,因此性能是较低的。但在Melang中,底层解释器是纯异步执行的。

举个简单的例子:

//a.mln
while (true) {
  @mln_msgQueueSend('queue', 'a');
  ret = @mln_msgQueueRecv('queue');
  @mln_print(ret);
}
//b.mln
while (true) {
  ret = @mln_msgQueueRecv('queue');
  @mln_print(ret);
  @mln_msgQueueSend('queue', 'b');
}

这两个脚本任务的是利用消息队列函数互发消息互收对方消息并打印出来。对于每一个任务而言,它的逻辑都是同步的顺序的,例如a.mln就是一定是先发消息,然后收消息,然后打印。只是a在接收时,如果b并未给a发消息,则a会挂起等待。

当然,上面的例子只是一个简单的例子,涉及到网络IO的情况也是一样的。当套接字上有数据到达时,解释器会接收,然后返回给上层脚本,并将该脚本从挂起态改为执行态。

社区特性

Melang的核心库代码虽然已经有几年的积累,但社区还处于萌芽阶段。现今业界绝大多数开源项目都是依托某些名企崛起的,但也有一些知名项目是有个人推动发展起来的。

在这先简单讨论下这两者的利弊:

依托企业,优点自然就是推广相对容易,由企业做背书,用户相对放心。但缺点是容易造成版权纠纷而吃官司(参见网络上安卓使用java侵权甲骨文的相关文章),也就导致了项目的发展无法完全由社区推动,而是由公司主导。

依托个人,优点就是规避了公司层面的侵权问题,可以极大地利用社区优势对项目进行扩张更新。缺点就是,前期推广很难,因为缺少名企背书,导致用户使用时依旧心怀疑虑担心稳定性安全性等问题。但是,这类项目也不乏成功先例,例如Vue。

目前Melang项目仍是个人运作,未来也是期望能够发展为社区主导。

使用场景

目前Melang的使用场景主要还是嵌入其他应用,这与LUA基本相同。Melang在Github上的项目就是一个启动器,启动器会调用核心库中脚本相关函数进行初始化和执行。当然,核心库也是开源的

Melang的核心库中提供了很多算法、数据结构、事件机制以及多进程多线程框架等接口,开发人员依托该函数库进行开发不仅可以让自身系统增加脚本功能,还将会节省较大开发成本。

此外,若一个系统嵌入该核心库并使用了该核心库的事件接口来做异步处理,那么该系统在同一线程内,不仅可以做事件处理,还可以执行脚本,两者会交替进行,不会导致一方被阻而无法处理。

当然,也可以单独使用Melang。Melang提供了基础库,除了常规字符串和文件系统以及网络IO操作外,还提供了访问MySQL8的函数以及各类加解密、通信等一系列功能。

小结

总体而言,虽然核心库经历了若干年变迁,但语言在行业内还是处于萌芽阶段。因为定位于嵌入系统这一领域,因此也弱化了作为独立语言而基础库不够强大的短板。

此外,这款语言的核心库中是自行实现了编译原理相关的所有组件——词法分析器、语法解析器生成器等,且使用极为方便,无需命令生成代码文件。

顺便说一句,这是一款国人开发的语言喔,期待各位多多关注多多支持。

感谢阅读,如果您有好的想法或者意见建议,欢迎在下方留言评论或者私信码哥。

上一篇:安卓Java接口方法数过多


下一篇:一文了解python的 @property