Fast Messenger 是一种高级并发编程模式

[来源] 达内    [编辑] 达内   [时间]2012-09-12

在编程界,当用“高级”来形容一种编程语言时,那是表示这种编程语言含有接近人类语言的结构和语法。或者反过来说,在坐标另一轴的“低级”表示一种编程语言的结构和语法更直接反映硬件的特性,比如:机器代码及其体系结构。

高级编程语言

在编程界,当用“高级”来形容一种编程语言时,那是表示这种编程语言含有接近人类语言的结构和语法。或者反过来说,在坐标另一轴的“低级”表示一种编程语言的结构和语法更直接反映硬件的特性,比如:机器代码及其体系结构。

高级并发编程模式

这个概念启发我建立了一个类似的标准来描述并发编程的模式和技术。“高级”用来表示一种并发编程模式的思维方式更接近人类的,而“低级”则表示更接近机器的思维方式。

评价一种模式是高级还是低级,一般要观察它的静态特性和动态特性。静态特 指的是它有哪些结构可以给程序员用来构建程序,这些结构是更象人类的思考模式还是机器的。比如一个整数数组,甲语言要求全是整数;乙语言里可以是整数,也可以容忍string形式的整数。那显然乙语言更象人类的思维,更高级。动态特性就是这些结构能被组织和互动的方式是否更象人类还是机器。除了模式本身外,我们还可以从使用的角度来评介。比如程序员如果用一种并发模式写程序时觉得设计和思考起来更自然,那就是更高级了。

如果一种模式的静态和动态特性更象是机器的特性的反映,程序员用起来的时候更觉得是在迁就机器,那就是低级的模式了。

高级编程语言一般都可以用更低级的编程语言来实现,同样高级的并发编程模式也可以用更低级的并发模式来实现。

线程模式

线程模式包含线程和它的堆栈。线程是CPU的执行代码的能力,只在运行的时候出现,在源代码中体现不出来。一个线程按照地址或源程序的顺序依此执行代码。在进入一个子程序或method之前,它会把当前地址存入堆栈。这样当子程序执行完毕,它就可以回到原来的地方继续执行代码。

根据线程的本质和高低级的定义,线程模式就是一种几乎是最低级的并发编程 式,因为多线程本来就是CPU的特性。CPU和应用程序员之间还有操作系统和语言运行系统,比如JVM和CLR。线程是被操作系统和语言运行系统直接支持的,应用程序员使用的线程和CPU里的线程它们的编程特性几乎是一样的。(虽然每一层都会对线程作一次映射,并且高一层通常有更多的线程数量。)

图 1:用线程编程时的思维模型

当用线程来编程时,我一般会写好业务逻辑代码,最后创建一个线程来运行这 逻辑并得到结果。我的思维过程中一直是以业务逻辑为中心的,线程只是为了执行这些逻辑的,是附属与逻辑代码的。图 1 用冲咖啡来比喻我的思维模型,因为两者非常类似。准备水、咖啡粉和咖啡机就象是写业务逻辑,电源插座就是线程,咖啡机插上电源就是线程开始执行业务逻辑代码。

线程编程 冲咖啡
 1,编写业务逻辑  1,准备水、咖啡粉和咖啡机
 2,创建一个线程  2,找一个电源插座
 3,用线程执行业务逻辑  3,给咖啡机插上电源

线程池模式

除非你的程序很大,象一个自动售货机一样,上一次货后可以接受多个购买请求,否则你要为每一个业务逻辑创建一个线程。当你有很多小的业务逻辑,单纯地使用线程模式将会领编程很麻烦。于是开发了线程池,可以通过重用来更有效地使用线程。

这里的线程池指的并不是简单的把线程放在一起,而是指通过把线程管理起来 提供一个API来让程序员可以提交任务,并执行这些任务。这样的话,线程池就变成了一个类似第三方的服务。这个服务接受任务,并返回一个token(比如Java中的Future)给调用者,让它可以随时了解它提交的任务在线程池中的执行情况。

线程池模式要比单纯的线程模式要更高级,因为它所建立的接受任务的服务形 了一种不存在于机器底层的抽象特性,这种抽象特性更接近程序员的思维方式。使用这种模式的时候,程序员不仅仅专心于业务逻辑,还要考虑线程池这个重要的角色,并要考虑多个任务围绕着线程池,以及如何互相协作的问题。

图 2:用线程池编程时的思维模型

在线程模式中,一个线程可以看成是业务逻辑的一个附属,它的全部用途就是 行该业务逻辑。而在线程池模式中则有所不同,一个线程池不是仅为一个业务逻辑服务的,整个线程池是一个独立的实体。程序员在编程的思维过程中必须同时考虑多个任务和线程池,线程池不是任务的附属。而且线程池的独立性也反过来影响到对任务的设计,比如一个大任务可以分解成多个更小一点的任务,由此又增加了任务的通讯的需求。

FastMessenger模式:先通过一个例子来介绍该模式

Fast Messenger 是一个半年新的编程模式(看成工具库也行),可以用来并发编程。我先通过展示一个简单的例子,来快速介绍一下Fast Messenger的程序看起来是什么样子的。之后我们再来分析该模式是不是一个并发的编程模式?是高级还是低级?

 1
 public class
 Fibonacci {  2
     static
 HashMap<BigInteger, BigInteger> cache = new

 HashMap<BigInteger, BigInteger> (); 
 3

     static
 IMessenger messenger; 

 4
     
 5     public
 static void
 main (String[] args) {  6
         Worker[] workers = new
 Worker[] { new

 Worker (), new Worker () }; 
 7
 
 8         messenger = new
 Messenger ();  9
         messenger.registerReceiver (workers, "worker", "multiply"); 
10
         
11         BigInteger N = 
new BigInteger ("5000000"); 


12         fibonacci (N); 
13     } 14
 
15     public
 static
 BigInteger fibonacci (BigInteger n) { 

16         if
 (n.equals ()) 
17

             return
 ; 

18         if
 (n.equals ()) 
19

             return
 ; 

20         if
 (cache.containsKey (n)) 21
             return
 cache.get(n); 

22
 
23         if
 (n.testBit (0)) { 24
             BigInteger n2 = n.shiftRight (1); 
25

             BigInteger n3 = n2.add (); 
26             BigInteger fn2 =
 fibonacci (n2); 

27
             IResponse<BigInteger> fn2xn2 = messenger.sendRequest ("worker", "multiply", fn2, fn2); 

28             BigInteger fn3 =
 fibonacci (n3); 29
             IResponse<BigInteger> fn3xn3 = messenger.sendRequest ("worker", "multiply", fn3, fn3); 
30

             BigInteger result = fn2xn2.get ().add (fn3xn3.get ()); 
31

             cache.put (n, result); 
32             return
 result; 

33         } else
 { 34
             BigInteger n2 = n.shiftRight (1); 
35

             BigInteger n3 = n2.subtract (); 
36             BigInteger sum =
 fibonacci (n2).add (fibonacci (n3)).add (fibonacci (n3)); 

37             BigInteger result =
 fibonacci (n2).multiply (sum); 

38 
            cache.put (n, result); 

39             return
 result; 40
         } 41
     } 42
 } 43
 
44 public
 class Worker { 
45     public
 BigInteger multiply (BigInteger a, BigInteger b) { 
46

         return a.multiply (b); 47     } 
48 }

源程序列表1:Fibonacci function

原程序来源于一本计算机的教科书,叫做“Introduction to Programming in Java: An Interdisciplinary Approach”( Messenger改写过了的程序。

Fast Messenger采用了“中间人”的设计。你必须通过一个messenger(中间人) 能给其他的object发送message或request。在main()中,创建了一个messenger的instance,并在该messenger上用同一个名字“worker”注册了2个Worker的instance。名字后面的“multiply”用来表明worker instance可以接受和处理的message/request类型。Fast Messenger支持reflection,所以在这个例子里,multiply直接就是Worker class里method的名字。

当这个messenger设置好了后,就可以为你代发request了:

... fn2xn2 = messenger.sendRequest(...);

这行代码表示fibonacci()作为caller,通知messenger发送一个request给一个叫“worker”的callee,也就是之前注册的worker instance。然后参数,比如fn2,fn2,或fn3,fn3,将被messenger转发给worker的multiply()。

这个sendRequest()会立即返回一个IResponse给caller,也就是fibonacci()里的27或29行。然 caller会在sendRequest()之后和callee的multiply()并发运行。这个caller可以在它认为合适的时候调用IResponse.get()来得到multiply()的结果,因为这个get()是一个block调用。你如果不希望block caller的主流程的话,可以调用IResponse里的其他非block method来检查运行状态。确定结果准备好了再用IResponse.get()。

Fast Messenger 是一个高级的并发编程模式

本文不是以介绍Fast Messenger的内容细节为主,我们将只评价这个例子程序涉及到的Fast Messenger的特性,来看看它是不是一个高级的并发编程模式。

在例子里可以看到FastMessenger的三个主体:messenger,sender,和receiver。一个sender,也就是caller,可以是任何一个普通的object,但receiver必须事先在messenger上注册。这个messenger站在sender和receiver的中间,负责转发request给预先注册过的receiver。

你也许已经留意到了,这个例子程序和使用线程池的程序在结构上非常相似。这个是有原因的,Fast Messenger为了吸引使用线程池程序员,专门对自己有针对性的修改过,使得使用Fast Messenger和使用线程池非常类似。

不象线程池模式是从低级(线程模式)到高级的发展模式,Fast Messenger从一开始就是从OOP出发,将object加强成active object(也就是之前提到的receiver)。并人类典型的思维方式学习,确定了用异步的消息作为核心,同时使用辅助的API来提供同步的IResponse。使得Fast Messenger既能并发,又保持了易用性。

每一个receiver都有一个唯一的名字,并能接受多种message/request(当然逻辑得由应 用程序员提供。)一般来说,caller是不需要message的结果的,当需要request的结果。这也是为什么一个叫message,一个叫request。这种互动的方式和我们在现实中和同事沟通、协作的方式非常类似。既能互相要求、通讯,但执行的时候又是独立的,各个有各自的大脑和手脚。

在一个receiver中,message和request是按顺序发送过来的,一次保证只处理一个。这 ,receiver内部是单线程的,没有并发的race condition。这和人工作的方式再一次的很接近,人脑在短时间内(在秒级)是不能多任务的,人只能在一个大的时间段上通过分时来多任务。这样的好处是程序员在receiver内写业务逻辑将没有race condition的后顾之忧。

如果一个receiver一次只能处理一个message,那如何提高处理能力呢?Fast Messenger的设计是允许一个receiver名字下注册多个instance。Sender是按名字发message的,Fast Messenger将在内部把message分给空闲的instance。这个设计也是从现实中借鉴来的,一个工作人员不够,就加人,组成一个team嘛。

图 3:用Fast Messenger编程时的思维模型

Fast Messenger提供给你一个接近现实社会、接近人类思维方式的并发模式。它把死 的程序代码和活的执行能力组合在一个统一的概念之中,叫做active object,直接扩充了OOP在并发方面的空白(线程不是OOP的一部分。)在图3的思维模型中,你不用再将咖啡机和电源分开来考虑了,而是直接把它当成一个人来考虑。这个人有大脑(业务逻辑),有手脚(收到message/request就能执行,不需要caller的干预。)

这样的话,你只需要告诉receiver你需要什么就可以了(当然receiver内部也是需要 程的。)比如说要一杯咖啡,只要发一个合适的request,并收好回执(IResponse),然后在你方便的时候就可以去把咖啡端回来了。这个receiver内部会找到一个线程,然后用这个线程独立地准备好水、咖啡粉、和咖啡机,最后加电冲咖啡。

资源下载