在 OO 语言写的程序里面,object 之间最基本的动作是同步功能调用(synchronous method invocation)。Fast Messenger 编程方法在不改动这个基本动作的前提下,实现了 object 之间的(虚拟)异步功能调用(asynchronous method invocation)。其基本思想是在两个 object 之间插入一个中间人 object,然后用两个同步功能调用模拟出一个异步功能调用。FM 将一些众所周知的编程元素(比如编程模式,编程小窍门等)以创新的方式组织起来,达到了这个目的。
本文将这些编程元素列举出来,并配上简单说明。
OOP原装的同步功能调用
同步功能调用看起来好像就只有一个动作,其实它隐含了好些前提和步骤:
在 object s 中必需要有一个指向 object r 的指针;
在 object s 中必需要有一个正在运行的 thread t;
Thread t 使用那个指针调用 object r 上的 method,进而执行细节步骤:
Thread t 离开 object s,同时进入 object r;
Thread t 在 object r 中执行那个 method 中的代码;
Thread t 执行完那个 method 后,退出 object r;
Thread t 重新进入 object s;
Thread t 回到 object s 后,从刚才离开的地方继续运行。
示意代码:
Class S { Class R { R r; // thread t // thread t …… void method (args) { r.method (args); …… …… } } }
Messenger Object
FM 使用了中间人设计,在 object s 和 r 之间插入了一个 messenger object m。Object m 将 object s 和 r 分隔开,形成两个区间:一个包含 object s 和 m;另一个包含 object m 和 r。虽然在同一个区间里的 object 还是用 OOP 的同步功能调用来互相动作,但两个区间合作起来,却在 object s 和 r 之间形成了一个虚拟的异步功能调用。
这个 object m 为 FM 贡献了两个重要的基础。第一个是建立在 object s 和 r 之间的异步特性。当 object s 调用 object m 时,object m 将传过来的参数保存起来,供稍后处理。这样,object s 所使用的 thread t1 可以立即返回到 object s。然后在 object m 的内部,使用另外的 thread t2 来处理刚才收到的参数,并用它们来调用 object r。
第二个是给 object r 创建了一个单线程环境(single-thread context),因为 object m 的位置,使得它可以完全控制进入 object r 的线程数量。这样 object r 里的代码可以不用考虑线程安全(thread safe)问题。
示意代码:
Class S { Class R { Messenger m; // any thread but only one a time R r; void method (args) { // thread t1 …… …… } m.call (r, method, args); } …… }
Object ID
Object m 已经去掉了 object s 和 r 之间的一个耦合,因为原先 object s 是直接调用 object r 的。Object ID 进一步去掉了它们间的另一个耦合,现在 object s 都不需要一个指向 object r 的指针了,object s 只需要知道 object r 的 object ID 就够了。指针是依赖于硬件和所用的 OO 语言的,而 object ID 可以是文本,字符,和数字等,甚至是人都可以阅读、理解、和直接使用的。
因为多了一层 ID 到指针的映射关系(即 mapping 或 binding),object m 需要把所有的映射管理起来。这样在 object s 用 “r” 来调用 object r 之前,“r” 到指针 r 的映射必须提前告诉 object m,就如下面的代码所示。
示意代码:
Messenger m = new Messenger (); m.register (“r”, r); Class S { Class R { Messenger m; // any thread but only one a time void method (args) { // thread t1 …… …… } m.call (“r”, method, args); } …… }
Message Ports
Object ID 这个概念在 object 和指针之上建立了一个抽象层,这样在模型这一级,object r 已经不存在了,object s 看见的只有 “r”。Message port 是个类似的概念,它在 method 之上建立了一个抽象层。Object s 将用一个 message port (比如下面代码中的 “x”)来指明它要在 object “r” 上调用的功能。
如果你熟悉 message passing 的话,可以从另一个角度来理解 message port。FM 不对单个的 message 提供辨认的方法,一个 message port 可以用来辨认一组类似的 message:它们的数据类型,格式,语义等都是一致的。
示意代码:
Messenger m = new Messenger (); m.register (“r”, r, array of ports); Class S { Class R { Messenger m; // any thread but only one a time void onMessage (port, args) { // thread t1 if (port == “x”) { …… …… m.send (“r”, “x”, args); } else if …… …… } } }
Object ID Instances
Object ID 是个在 object 之上的抽象概念,一个 ID 可以代表一个 object,也可以代表多个 object,甚至其它的什么东西。所以 Object ID instance 里的 instance 特指该 ID 代表的 object 中的一个。这个名字取得不是很好,因为 object 和 instance 在 OOP 里是可以互换的,以后有好的名字再说了。
前面提到的单线程环境(single-thread context),是应用到 instance 这一级的。当一个 ID 后面有多个 object (也就是 instance),那么 object m 每次收到一个对该 ID 的调用请求,都会找一个空闲的 object 用一个不同的线程来调用。这样,如果一个 ID 有 N 个 instance 的话,那最多可能有 N 个不同的线程各自在一个 instance 上运行。
示意代码:
Messenger m = new Messenger (); m.register (“r”, array of objects, array of ports); Class S { Class R { Messenger m; // any thread but only one a time void onMessage (port, args) { // thread t1 if (port == “x”) { …… …… m.send (“r”, “x”, args); } else if …… …… } } }