主动对象模式(Active Object Pattern)
主动对象模式用于降低方法执行和方法调用之间的耦合。该模式描述了另外一种更为透明的任务间通信方法。
该模式使用
ACE_Task类作为主动对象。在这个对象上调用方法时,它就像是常规对象一样。就是说,方法调用是通过同样的->操作符来完成的,其不同在于这些方法的执行发生于封装在ACE_Task中的线程内。在使用被动或主动对象进行编程时,客户程序看不到什么区别,或仅仅是很小的区别。对于构架开发者来说,这是非常有用的,因为开发者需要使构架客户与构架的内部结构屏蔽开来。这样构架用户就不必去担心线程、同步、会合点(rendezvous),等等。5.3.1 主动对象模式工作原理
主动对象模式是
ACE实现的较为复杂的模式中的一个。该模式有如下参与者:
我们已经看到,
ACE_Task是怎样创建和封装线程的。要使ACE_Task成为主动对象,需要完成一些额外的工作:必须为所有要从客户异步调用的方法编写方法对象。每个方法对象都派生自
ACE_Method_Object,并会实现它的call()方法。每个方法对象还维护上下文信息(比如执行方法所需的参数,以及用于获取返回值的ACE_Future对象。这些值作为私有属性维护)。你可以把方法对象看作是方法调用的“罩子”(closure)。客户发出方法调用,使得相应的方法对象被实例化,并被放入启用队列(activation queue)中。方法对象是命令(Command)模式的一种形式(参见有关设计模式的参考文献)。ACE_Activation_Queue
是一个队列,方法对象在等待执行时被放入其中。因而启用队列中含有所有等待调用的方法(以方法对象的形式)。封装在ACE_Task中的线程保持阻塞,等待任何方法对象被放入启用队列。一旦有方法对象被放入,任务就将该方法对象取出,并调用它的call()方法。call()方法应该随即调用该方法在ACE_Task中的相应实现。在方法实现返回后,call()方法在ACE_Future对象中设置(set())所获得的结果。客户使用
ACE_Future对象获取它在主动对象上发出的任何异步操作的结果。一旦客户发出异步调用,立即就会返回一个ACE_Future对象。于是客户就可以在任何它喜欢的时候去尝试从“期货”(future)对象中获取结果。如果客户试图在结果被设置之前从期货对象中提取结果,客户将会阻塞。如果客户不希望阻塞,它可以通过使用ready()调用来轮询(poll)期货对象。如果结果已被设置,该方法返回1;否则就返回0。ACE_Future对象基于“多态期货”(polymorphic futures)的概念。call()
方法的实现应该将返回的ACE_Future对象的内部值设置为从调用实际的方法实现所获得的结果(这个实际的方法实现在ACE_Task中编写)。下面的例子演示主动对象模式是怎样实现的。在此例中,主动对象是一个“
Logger”(日志记录器)对象。Logger使用慢速的I/O系统来记录发送给它的消息。因为此I/O系统很慢,我们不希望主应用任务的执行因为相对来说并非紧急的日志记录而减慢。为了防止此情况的发生,并且允许程序员像发出普通的方法调用那样发出日志调用,我们使用了主动对象模式。Logger
类的声明如下所示:
例
5-3a//The worker thread with which the client will interact
class Logger: public ACE_Task
{
public:
//Initialization and termination methods
Logger();
virtual ~Logger(void);
virtual int open (void *);
virtual int close (u_long flags = 0);
//The entry point for all threads created in the Logger
virtual int svc (void);
///////////////////////////////////////////////////////
//Methods which can be invoked by client asynchronously.
///////////////////////////////////////////////////////
//Log message
ACE_Future
//Return the name of the Task
ACE_Future
///////////////////////////////////////////////////////
//Actual implementation methods for the Logger
///////////////////////////////////////////////////////
u_long logMsg_i(const char *msg);
const char * name_i();
private:
char *name_;
ACE_Activation_Queue activation_queue_;
};
如我们所看到的,
Logger主动对象派生自ACE_Task,并含有一个ACE_Activation_Queue。Logger支持两个异步方法:logMsg()和name()。这两个方法应该这样来实现:当客户调用它们时,它们实例化相应的方法对象类型,并将它放入任务的私有启用队列。这两个方法的实际实现(也就是“真正地”完成所需工作的方法)是logMsg_i()和name_i()。下面的代码段显示我们所需的两个方法对象的接口,分别针对
Logger主动对象中的两个异步方法。
例
5-3b//Method Object which implements the logMsg() method of the active
//Logger active object class
class logMsg_MO: public ACE_Method_Object
{
public:
//Constructor which is passed a reference to the active object, the
//parameters for the method, and a reference to the future which
//contains the result.
logMsg_MO(Logger * logger, const char * msg,
ACE_Future
&future_result);
virtual ~logMsg_MO();
//The call() method will be called by the Logger Active Object
//class, once this method object is dequeued from the activation
//queue. This is implemented so that it does two things. First it
//must execute the actual implementation method (which is specified
//in the Logger class. Second, it must set the result it obtains from
//that call in the future object that it has returned to the client.
//Note that the method object always keeps a reference to the same
//future object that it returned to the client so that it can set the
//result value in it.
virtual int call (void);
private:
Logger * logger_;
const char* msg_;
ACE_Future
};
//Method Object which implements the name() method of the active Logger
//active object class
class name_MO: public ACE_Method_Object
{
public:
//Constructor which is passed a reference to the active object, the
//parameters for the method, and a reference to the future which
//contains the result.
name_MO(Logger * logger, ACE_Future
virtual ~name_MO();
//The call() method will be called by the Logger Active Object
//class, once this method object is dequeued from the activation
//queue. This is implemented so that it does two things. First it
//must execute the actual implementation method (which is specified
//in the Logger class. Second, it must set the result it obtains from
//that call in the future object that it has returned to the client.
//Note that the method object always keeps a reference to the same
//future object that it returned to the client so that it can set the
//result value in it.
virtual int call (void);
private:
Logger * logger_;
ACE_Future
};
每个方法对象都有一个构造器,用于为方法调用创建“罩子”(
closure)。这意味着构造器通过将调用的参数和返回值作为方法对象中的私有成员数据记录下来,来确保它们被此对象“记住”。调用方法包含的代码将对在Logger主动对象中定义的实际方法实现(也就是,logMsg_i()和name_i())进行委托。
