This article is about a multi-threading technique. For the
lockstep protocol variant, see
Active objects.
The active object design pattern decouples method execution from method invocation for objects that each reside in their own thread of control.[1] The goal is to introduce concurrency, by using asynchronous method invocation and a scheduler for handling requests.[2]
The pattern consists of six elements:[3]
- A proxy, which provides an interface towards clients with publicly accessible methods.
- An interface which defines the method request on an active object.
- A list of pending requests from clients.
- A scheduler, which decides which request to execute next.
- The implementation of the active object method.
- A callback or variable for the client to receive the result.
Command模式的关键在于只包含一个 Execute方法,子类在实现这个接口时,在Execute方法中,完成特定的任务。可以说,这是一个非常简单的模式。
《ASD》中提到了该模式的三种用法:
1. Invoker可以和任意一个Command挂钩,而且不需要了解这到底是个什么ConcreteCommand,然后在需要的时候调用这个 Command对象的Execute方法就行了。这在消息驱动的的系统中非常常见,每个trigger就是一个invoker。那么如何把Command 和invoker挂钩呢?方法很多,最cool的方法是在系统外用一个配置文件来指定。这样不需要重新编译就可以改变软件运行的方式。可以参考 Source Insight的界面。Source Insight中可以任意配置菜单项和工具栏按钮。其实现应该就是应用了这种Command模式。
2. 上面的方法是否让人想起了Template模式?有点相像吧。顺着这个思路去想,就可以把Command模式应用于Transaction。让一个类来解决Transaction的init和uninit问题,中间包含一个Command的队列。这样就可以把这个队列中的全部command当作一个 transaction了。这样的作法可以把Transaction的实现和逻辑分离开来,是很漂亮的实现。同样的思路,也可以用在类似的问题上,需要 init和uninit,中间有不定量的操作。
3.如果真的用来解决transaction问题,那么就必须具备roll back的能力。然而这个很容易实现,只要在command类中,添加undo方法就可以了。剩下的活交给invoker来处理。
4.此外还有一个附带的好处。command类和一个单独的execute方法其实很相似,但是command类的对象有生命周期,可以由程序来控制。因此,一个command对象,可以在提交了很长时间以后再批量执行。
除了这些以外,《Design Pattern》还提到了Command模式的其他使用方法。虽然这些方法未必实用,但我还是把它们列在这里:
1.command对象和command对象的序列都可以serialization。这样如果软件被有意或无意的中止(例如crash),在重新启动后,还可以接续之前没有完成的任务。
2.Command模式如果和Composite模式接合,就可以作出MacroCommand。^_^,这个idea虽然很cool,但是可以用到的地方大概不多吧。
Active Object模式不属于《Design Pattern》23模式。实际上,她是一种特殊的Command Queue。其特殊之处在于:
1. 队列的拥有者会顺序地执行队列中所有Command对象的Execute方法。(这个其实不算特殊)
2.Command对象在自己的Execute方法结束前,可以把一个新的command对象(实际上常常是这个command对象自己)再加到队列的尾部。
看出来了吗,这个队列有可能不会终止的,他可以一直执行下去。这个可以作为一个应用或者服务的主模块了,想像一下她可以作多少事情吧。
《ASP》指出这个模式可以用来在一个线程中处理多任务的问题!!! ^_^ 太cool了。
如何处理呢?你可以把每个command对象看作是一个任务。他在Execute函数中,处理自己的任务,在任务告一段落时,记录自己的状态,然后把自己插入到队列的尾部,结束Execute方法。当队列轮完一周后,又会再次执行这个command对象的Execute方法。 ^_^ 很cool吧。
那么这种方法和多线程的方法相比有什么有缺点呢?
最大的优点是,所有的command都在同一个线程中,因此切换时,不需要进入内核模式!!超高效啊!!而且,可以有很多很多的command,数量上远远超过多线程的数量。
缺点嘛,是这种方法需要用户自己来实现调度,另外这其实是一种非剥夺模式的多任务,如果command处理不好,就会连累其它所有的command,因此实际上比多线程要更复杂。(嘿嘿,程序员能够怕麻烦吗?)
还有一点,Active Object运行于单线程,也就是说,她不能享受多处理器或多处理器核心带来的性能上的改善。
其实,这最后一点是非常致命的一点。也就是说,在当前intel的超线程CPU机器上,如果系统的负担并不重的时候。Active Object的效率有可能比多线程更低。
Anyway,这是一个非常有趣的模式。只是一般的程序员可能没有机会用到。但是请记住她,也许能有那么一次机会,可一用她来爽上一把。