`

设计模式- 主动对象(Active Object)

 
阅读更多
译者注:
1.    对象分为主动对象和被动对象,主动对象内部包含一个线程,可以自动完成动作或改变状态,而一般的被动对象只能通过被其他对象调用才有所作为。在多线程程序中,经常把一个线程封装到主动对象里面。
2.    在翻译过程中,发现的原文不妥处被更正。
3.    原文中许多内容一再重复,颇有蛇足之感,取精用宏,删繁就简。
4.    尝试更高程度的意译。
关键词
这个文章介绍主动对象模式,主动对象是内部拥有自己的控制线程的对象。为了简化异步调用的复杂性,这个模式分离了方法的执行和调用。使用这个模式,一个对象中无论是否有独立的线程,客户从外部访问它时,感觉是一样的。生产者/消费者,读者/写者,这两个应用广泛的模型中,这种策略非常适合。这个模式常用在多线程的,分布式系统中。另外,一些客户端应用,如:视窗系统和网络浏览器,也可以使用主动对象模式,对并行的、异步调起的IO操作简化处理。

1    目的
主动对象模式隔离了方法执行和方法调用的过程,提高了并行性,对内部拥有控制线程的主动对象,降低了异步访问的复杂性。
2    别名
并行行为对象(Concurrent Object and Actor)
3    例子
为了说明主动对象模式,考虑一个通信网关的设计。网关隔离互相协作的多个组成单元,让交互过程不直接依赖于对方。参照图1,一个分布式系统中,来自多个生产者(卫星设备)的消息,被网管转发给多个消费者(局域网内的主机)。


我们的例子中,生产者和消费者使用TCP协议通信,这是面向连接的通信协议。网关上的进程向消费者发送数据时,操作会发生阻塞。这是因为,网络传输能力有限,TCP进行流量控制,防止过量的数据不能及时缓冲和处理。

要提高整体的效率,网关处理进程不能因一个连接上的阻塞而等待。另外,当生产者和消费者的数目增加的时候,整个进程必须相应的增加处理能力(译者注:通过增加处理线程)。

一个提高性能的有效方法,就是使用并行。应用并行后,服务对象拥有独立线程,线程实际完成操作,和方法调用的过程分开。并且,不同线程处理不同TCP连接,一个连接上的线程被阻塞,不会影响到其他连接的线程。
4    场景
对象的访问者(Client,下统称为客户)和对象的实现在不同的线程中。
5    问题
许多应用场景,服务者并行处理多客户端的请求,提高服务的质量(QoS)。被动对象在客户线程中完成操作过程,主动对象使用专用的线程完成。一个对象的数据被多个线程共享时,必须处理好线程的同步。这导致三个约束。

1.    对一个对象调用不能阻塞整个进程,不能影响其他线程的执行:比如,通信网关的例子中,一个TCP连接上的数据传送被阻塞,整个进程仍然能继续处理。同样,其他没有被阻塞的网络连接,应该正常的发送数据。
2.    对共享对象同步访问的逻辑应该简化:客户在使用共享对象地时候,如果面对底层的同步机制,必须记得先要获取互斥锁,后要释放互斥锁,编码就比较麻烦。一般情况,共享对象的方法隐藏这些细节,当多个客户线程访问同一个对象时,这些约束是透明的。
3.    存在可以平行进行的操作,设计成同时执行:在通信网关的例子中,同时使用多个TCP连接给不同的消费者发送数据。如果网关的进程在单个线程上执行,及时使用多个处理器不能明显的提高性能。
6    方案
对每一个要求并发执行的对象,分离其方法的调用和执行。这样,这个对象的客户就像是调用一个常规的方法一样。这个方法,自动把任务交给另外的线程完成执行。

主动对象的组成:一个代理者(Proxy)实现外部的访问接口;一个执行者(Servant)。代理和执行者在不同的线程执行,分离方法调用和执行的过程:代理者在客户线程中被调用执行,执行者在另外的线程完成操作。运行时,代理者把客户的调用信息封装“调用请求”(Method Request),通过调度者(Scheduler)把这个请求放到一个活动队列(Activation Queue)。调度者和执行者运行在另外的线程中,这个线程启动后,不断地从活动队列中得到“调用请求”对象,派发给执行者完成客户请求的操作。客户调用代理者后马上得到一个预约容器(Future),今后可以通过这个预约容器得到返回的结果。
7    结构
下面使用Booch风格的类图,对主动对象的组成结构进行说明。(译者注:这是Booch在《面向对象的分析和设计》书中使用的类图风格)

在这个模式中,共有六个参与者。
    代理者(Proxy)
    代理者定义了被客户调用的接口。这个接口是函数调用的方式,而不是像传统线程通信,使用数据传递的方式。当函数被调用的,代理者构造一个“调用请求”对象,并把它放到活动队列中,这一切都发生在客户线程中。

    调用请求(Method Request)
    “调用请求”用来保存相关函数调用的部上下文信息,比如函数标识,函数的参数,这些信息将在不同线程间传递。一个抽象的“调用请求”类,定义了执行活动对象方法的接口Call。并且包含一个Guard函数,Guard用来检查调用的条件是否满足。对代理者提供的每一个主动对象方法,在访问其执行者的时候需要条件判断。代理者被调用的时候会创建具体“调用请求”对象,对象中包含了执行这个方法必须的参数和数据的返回方式。

    活动队列(Activation Queue)
    这个队列维护了一个缓冲区(译者注:不一定是先进先出的队列),缓冲区中存放了待执行的“调用请求”。正是这个队列分离可客户线程和执行操作的线程。

    调度者(Scheduler)
    调度者管理活动队列。调度者决定队列中的哪一个调用请求先被执行。调度的决定可能决定与多个规则:关键字顺序,入队的顺序,要满足的执行条件或等待发生的事件,如:在一个数据结构中出现空闲区。调度者使用Guard方法的调用,来检查是否满足执行条件。

    执行者(Servant)
    真正完成操作的对象。执行者实际完成代理者定义的主动对象方法,响应一个“调用请求”。调度者给“调用请求”分派一共执行者,然后调用“调用请求”中的Call方法。对应的执行者的方法将被调用。执行者的方法运行在调度者的线程中。执行者可能同时提供了一些方法供“调用请求”实现Guard。

    预约容器(Future)
    当执行者完成操作,客户通过预约容器获取返回结果。当客户调用代理者的方法后,一共空预约容器马上返回。预约容器指向一块内存空间,用来保存返回结果。客户可以通过轮训或阻塞调用的方法,通过预约容器得到返回结果。
8    运行
下面的图说明了一个调用过程中的三个阶段。
注×:原图中“enqueue(M1)”的位置有误,入队操作应该在返回Future之前,本图已经更正。黄色表现客户线程空间,绿色表示调度者线程空间。

1.    构造“调用请求”:在这个阶段,客户调用代理者的一个方法m1()。“调用请求”对象被创建,这个对象中包含了所有的参数。代理者把这个“调用请求”对象传递给调度者,调度者把它入队到活动队列。如果方法m1()有返回值,就返回一个预约容器(Future),否则不返回。
2.    调度执行:调度者的执行线程中,监控活动队列,当队列中的一个请求满足执行条件时,调度者把它出队,把一个执行者绑定到这个请求上。然后通过“调用请求”的Call方法,Call再调用执行者的m1(),完成客户请求的操作。
3.    完成:在这个阶段,如果有返回值,就把返回值存储到对应的预约容器中。然后调度者线程继续在活动队列中查找下一个要执行的“调用请求”。客户就可以在预约容器中找到返回值。当“调用请求”和“预约容器”不在使用的时候,注意销毁,防止内存漏洞。
9    实现
这一节说明一个主动对象模式使用的过程。这个应用是上面例子的部分实现。图2说明了各个组成部分。这节的例子,使用到了ACE框架的可重用组件。ACE提供了丰富的包装器和框架组件,用来完成软件间的通信任务,并且ACE是跨平台的。


1.    执行者的实现:我们的例子中,执行者是个消息队列,用来缓冲发送到消费者的消息。对每一个远程的消费者,对应一个Consumer Handler,Handler中包含一个到消费者进程的TCP连接(译者注:每一个主动对象要包装一个Consumer Handler)。每一个Consumer Handler对应的活动对象缓存的消息(译者:通过活动队列缓存“请求调用“的方式缓存消息),是从生产者发给网关的,并且等待网关把它发送给对应的消费者。下面的类定义了执行者的接口。
class MQ_Servant
{
public:
  MQ_Servant (size_t mq_size);
  // 消息队列实现的操作
  void put_i (const Message &msg);
  Message get_i (void);
  // 状态检查
  bool empty_i (void) const;
  bool full_i (void) const;
private:
  // 内部队列的实现,可能是循环数组,链表之类
};
put_i和get_i实现队列的插入和删除操作。另外的两个函数,empty_i和full_i用来检查队列的状态,队列共有三种状态,(1)空,(2)满,和(3)非空非满。这两个函数将帮助实现“调用请求”的Guard()。
注意,执行者MQ_Servant把线程同步的任务交给了外部。在我们的例子中,MQ_Servant没有包含任何线程同步的代码。这个类仅提供了检查其内部状态的方法。这种设计避免了“Inheritance anomaly”(继承反常)问题:如果规定了同步实现,会制约MQ_Servant被重用。而这样,同步方式的改变不影响MQ_Servant的实现(译者:放下即自在)。

2.    代理者和“调用请求”的实现:例子中,代理者MQ_Proxy提供了和执行者MQ_Servant一样的接口函数。另外,代理MQ_Proxy又是一个创造“调用请求”对象的工厂。下面是它的C++代码。
class MQ_Proxy
{
public:
  //  消息队列的长度
  enum { MAX_SIZE = 100 };
  MQ_Proxy (size_t size = MAX_SIZE)
    : scheduler_ (new MQ_Scheduler (size)),
  servant_ (new MQ_Servant (size)) {}
  // 调度<put> 在活动对象上执行
  void put (const Message &m) {
    Method_Request *method_request =
    new Put (servant_, m);
    scheduler_->enqueue (method_request);
  }
  // <Get>返回预约容器:Message_Future
  Message_Future get (void) {
    Message_Future result;
    Method_Request *method_request =
      new Get (servant_, result);
    scheduler_->enqueue (method_request);
    return result;
  }
  // ... empty() and full() 用来检查队列状态
protected:
  // 实际完成操作的执行者
  MQ_Servant *servant_;
  // 调度者
  MQ_Scheduler *scheduler_;
};
虚拟基类Method_Request,定义了“调用请求”的接口:
class Method_Request
{
public:
// 检查是否准备好
virtual bool guard (void) const = 0;
// 执行操作
virtual void call (void) = 0;
};
不同的请求,使用不同的子类定义
class Put : public Method_Request
{
public:
  Put (MQ_Servant *rep,
    Message arg)
    : servant_ (rep), arg_ (arg) {}
  virtual bool guard (void) const {
    // 约束检查
    return !servant_->full_i ();
  }
  virtual void call (void) {
    // 插入消息
    servant_->put_i (arg_);
  }
private:
  MQ_Servant *servant_;
  Message arg_;
};
上面的Guard函数,使用了MQ_Servant的full_i函数实现。
另外一个“调用请求”子类:
class Get : public Method_Request
{
public:
  Get (MQ_Servant *rep,
    const Message_Future &f)
    : servant_ (rep), result_ (f) {}
  bool guard (void) const {
    // Synchronization constraint:
    // cannot call a <get_i> method until
    // the queue is not empty.
    return !servant_->empty_i ();
  }
  virtual void call (void) {
    // Bind the dequeued message to the
    // future result object.
    result_ = servant_->get_i ();
  }
private:
  MQ_Servant *servant_;
  // Message_Future result value.
  Message_Future result_;
};
这个对象要使用预约容器,处理最终返回的结构。其内部保存了预约容器。

3.    活动队列的实现:每一个“调用请求”。一个典型的实现,是一个线程安全的缓冲区。一般还要实现遍历其元素的循环子(Iterator)。下面是本例的C++实现。
class Activation_Queue
{
public:
  // Block for an "infinite" amount of time
  // waiting for <enqueue> and <dequeue> methods
  // to complete.
  const int INFINITE = -1;
  // Define a "trait".
  typedef Activation_Queue_Iterator
  iterator;
  // Constructor creates the queue with the
  // specified high water mark that determines
  // its capacity.
  Activation_Queue (size_t high_water_mark);
  // Insert <method_request> into the queue, waiting
  // up to <msec_timeout> amount of time for space
  // to become available in the queue.
  void enqueue (Method_Request *method_request,
  long msec_timeout = INFINITE);
  // Remove <method_request> from the queue, waiting
  // up to <msec_timeout> amount of time for a
  // <method_request> to appear in the queue.
  void dequeue (Method_Request *method_request,
    long msec_timeout = INFINITE);
private:
  // Synchronization mechanisms, e.g., condition
  // variables and mutexes, and the queue
  // implementation, e.g., an array or a linked
  // list, go here.
  // ...
};
入队和出队的操作,是经典的“生产者、消费者”模型。很容易实现互斥访问。

4.    调度者的实现:调度者要一般实现一个入队操作。调度执行线程函数。一般作为静态函数存在,调用dispatch,实现调度执行线程。
class MQ_Scheduler
{
public:
  // Initialize the Activation_Queue to have the
  // specified capacity and make the Scheduler
  // run in its own thread of control.
  MQ_Scheduler (size_t high_water_mark);
  // ... Other constructors/destructors, etc.,
  // Insert the Method Request into
  // the Activation_Queue. This method
  // runs in the thread of its client, i.e.,
  // in the Proxy’s thread.
  void enqueue (Method_Request *method_request) {
    act_queue_->enqueue (method_request);
  }
  // Dispatch the Method Requests on their Servant
  // in the Scheduler’s thread.
  virtual void dispatch (void);
protected:
  // Queue of pending Method_Requests.
  Activation_Queue *act_queue_;
  // Entry point into the new thread.
  static void *svc_run (void *arg);
};

例子中,线程的启动和活动队列的创建都在调度者的构造函数里面:
MQ_Scheduler (size_t high_water_mark)
  : act_queue_ (new Activation_Queue
  (high_water_mark))
{
  // Spawn a separate thread to dispatch
  // method requests.
  Thread_Manager::instance ()->spawn (svc_run,
  this);
}

线程函数非常简单,就是调用Dispatch:
void *
MQ_Scheduler::svc_run (void *args)
{
  MQ_Scheduler *this_obj =
    reinterpret_cast<MQ_Scheduler *> (args);
  this_obj->dispatch ();
}

Dispatch的实现如下:
virtual void
MQ_Scheduler::dispatch (void)
{
  // Iterate continuously in a
  // separate thread.
  for (;;) {
    Activation_Queue::iterator i;
    // The iterator’s <begin> call blocks
    // when the <Activation_Queue> is empty.
    for (i = act_queue_->begin ();
    i != act_queue_->end ();
    i++) {
      // Select a Method Request ‘mr’
      // whose guard evaluates to true.
      Method_Request *mr = *i;
      if (mr->guard ()) {
        // Remove <mr> from the queue first
        // in case <call> throws an exception.
        act_queue_->dequeue (mr);
        mr->call ();
        delete mr;
      }
    }
  }
}

5.    异步调用以后,客户对返回结果的检测。调用活动对象的客户,如何获取和处理返回值?这有不同的策略。有下面三种返回值策略。
1)    同步调用,阻塞等待。客户线程阻塞,一直到操作完成、数据返回。
2)    同步调用,限时等待。客户线程阻塞,一直到数据返回、数据返回或者发生超时。
3)    异步调用。预约容器对象,提供某种异步方式返回数据或执行失败信息。
预约容器种的空间,被多个线程共享,当所有的线程都不再使用的时候,才能被清空内存。要特别注意。
在我们的例子种,Message_Future如下定义:
class Message_Future
{
public:
  // Copy constructor binds <this> and <f> to the
  // same <Message_Future_Rep>, which is created if
  // necessary.
  Message_Future (const Message_Future &f);aa
  // Constructor that initializes <Message_Future> to
  // point to <Message> <m> immediately.
  Message_Future (const Message &m);
  // Assignment operator that binds <this> and <f>
  // to the same <Message_Future_Rep>, which is
  // created if necessary.
  void operator= (const Message_Future &f);
  // ... other constructors/destructors, etc.,
  // Type conversion, which blocks
  // waiting to obtain the result of the
  // asynchronous method invocation.
  operator Message ();
};
可以使用引用计数的方法,处理Message的清除。

客户通过预约容器获取数据的两种方式:
立即方式:
MQ_Proxy mq;
// ...
// Conversion of Message_Future from the
// get() method into a Message causes the
// thread to block until a message is
// available.
Message msg = mq.get ();
// Transmit message to the consumer.
send (msg);

延迟方式:
// Obtain a future (does not block the client).
Message_Future future = mq.get ();
// Do something else here...
// Evaluate future in the conversion operator;
// may block if the result is not available yet.
Message msg = Message (future);
10    完成的例子
通信网关程序内部,包含生产者(Supplier)和消费者(Consumer)的Handler,它们分别是远程生产者和远程消费者的代理。如下面的图3所示,生产者的Handler从远程设备上接收消息,分析消息中的地址,根据消息中的地址查找路由表,确定哪一个远程的消费者应该接收这个消息。路由表维护地址到消费者Handler的影射关系。每个消费者的Handler实际通过对应的TCP连接把数据送出。

每一个消费者Handler使用上面讲的主动对象的模式,内部包含一个消息队列(译者:通过保存“调用请求”的活动队列间接实现了消息的缓存)。来实现消息的异步发送。
class Consumer_Handler
{
public:
  Consumer_Handler (void);
  // Put the message into the queue.
  void put (const Message &msg) {
    message_queue_.put (msg);
  }
private:
  // Proxy to the Active Object.
  MQ_Proxy message_queue_;
  // Connection to the remote consumer.
  SOCK_Stream connection_;
  // Entry point into the new thread.
  static void *svc_run (void *arg);
};

生产者的Handler,使用下面的方式,给消费者发送消息。
Supplier_Handler::route_message (const Message &msg)
{
  // Locate the appropriate consumer based on the
  // address information in the Message.
  Consumer_Handler *ch =
    routing_table_.find (msg.address ());
  // Put the Message into the Consumer Handler’s queue.
  ch->put (msg);
};
消费者的Handler,就是Cosumer_Handler,在构造函数里面创建其调度处理线程。
Consumer_Handler::Consumer_Handler (void)
{
  // Spawn a separate thread to get messages
  // from the message queue and send them to
  // the consumer.
  Thread_Manager::instance ()->spawn (svc_run,
  this);
}
下面是消费者Handler的线程函数的实现
void *
Consumer_Handler::svc_run (void *args)
{
  Consumer_Handler *this_obj =
  reinterpret_cast<Consumer_Handler *> (args);
  for (;;) {
    // Conversion of Message_Future from the
    // get() method into a Message causes the
    // thread to block until a message is
    // available.
    Message msg = this_obj->message_queue_.get ();
    // Transmit message to the consumer.
    this_obj->connection_.send (msg);
  }
}
当一个消费者的网络传送被阻塞的时候,只会阻塞其对应的线程,不会影响到其它消费者的Handler的处理。
11    变化
集成的调度者:
在实现主动对象模式的时候,为了减少对象的个数。可以把代理者和执行者的角色都分派到调度者身上。甚至“调用请求”的Call函数也可以由调度者实现。比如下面的代码,消息队列例子的集成实现方式。
class MQ_Scheduler
{
public:
  MQ_Scheduler (size_t size)
    : act_queue_ (new Activation_Queue (size))
  {}
  // ... other constructors/destructors, etc.,
  void put (const Message &msg) {
    Method_Request *method_request =
    // The <MQ_Scheduler> is the servant.
    new Put (this, msg);
    act_queue_->enqueue (method_request);
  }
  Message_Future get (void) {
    Message_Future result;
    Method_Request *method_request =
    // The <MQ_Scheduler> is the servant.
    new Get (this, result);
    act_queue_->enqueue (method_request);
    return result;
  }
  // ...
private:
  // Message queue servant operations.
  void put_i (const Message &msg);
  Message get_i (void);
  // Predicates.
  bool empty_i (void) const;
  bool full_i (void) const;
  Activation_Queue *act_queue_;
  // ...
};

这样集成后,减少了组件,实现更加简化。当然,这样也带来了缺点,调度者必须知道代理者和执行者的具体类型,具体实现。这样就很难在不同的活动对象中,重用调度者。

消息的直接传递:
更近一步的简化,代理者和执行者都删除掉。在客户线程和调度者线程之间直接使用数据的方式传递消息。
class Scheduler
{
public:
  Scheduler (size_t size)
    : act_queue_ (new Activation_Queue (size))
  {}
  // ... other constructors/destructors, etc.,
  // Enqueue a Message Request in the thread of
  // the client.
  void enqueue (Message_Request *message_request) {
    act_queue_->enqueue (message_request);
  }
  // Dispatch Message Requests in the thread of
  // the Scheduler.
  virtual void dispatch (void) {
    Message_Request *mr;
    // Block waiting for next request to arrive.
    while (act_queue_->dequeue (mr)) {
      // Process the message request <mr>.
    }
  }
  protected:
  Activation_Queue *act_queue_;
  // ...
};
因为没有了代理者,客户直接创建“调用请求”对象,然后调用调度者的函数把它入队到活动队列。同样的,没有了执行者,调度者的线程,在活动队列中得到请求,直接执行完成。

一般来说,这样这样实现的一个消息传递的机制,比实现一个主动对象要简单的多。消息传递这种复杂的逻辑直接暴露给其客户,不但增加开发的难度,还容易滋生BUG,这样想来,不如把这种逻辑封装在主动对象的内部。具体如何选择,根据实际情况和自己的喜好而定。

预约容器的泛型实现:
一个泛型的预约容器可以使用返回值的类型进行定制。预约容器实现了一个一次写多次读的同步机制。当容器中的值还没有准备好的时候,客户的访问操作被阻塞。这个泛型预约容器,部分实现了读者/写者模型,又部分实现了生产者/消费者模型。
下面是C++模板实现的例子
template <class T>
class Future
{
  // This class implements a ‘single write, multiple
  // read’ pattern that can be used to return results
  // from asynchronous method invocations.
public:
  // Constructor.
  Future (void);
  // Copy constructor that binds <this> and <r> to
  // the same <Future> representation
  Future (const Future<T> &r);
  // Destructor.
  ~Future (void);
  // Assignment operator that binds <this> and
  // <r> to the same <Future>.
  void operator = (const Future<T> &r);
  // Cancel a <Future>. Put the future into its
  // initial state. Returns 0 on success and -1
  // on failure.
  int cancel (void);
  // Type conversion, which obtains the result
  // of the asynchronous method invocation.
  // Will block forever until the result is
  // obtained.
  operator T ();
  // Check if the result is available.
  int ready (void);
private:
  Future_Rep<T> *future_rep_;
  // Future representation implemented using
  // the Counted Pointer idiom.
};
这个模板可以如下使用:
// Obtain a future (does not block the client).
Future<Message> future = mq.get ();
// Do something else here...
// Evaluate future in the conversion operator;
// may block if the result is not available yet.
Message msg = Message (future);

分布式活动对象:
代理者和调度者之间跨过网络。代理者把要把“调用请求”对象序列化,然后通过网络传输给另外机器上的调度者,调度者接收并再造“调用请求”对象。

使用线程池:
使用线程池,可以让一个活动对象支持多个执行者。多个执行者提供相同的服务。每一个执行者运行在不同的线程中,由调度者统一调度,当有新的请求时,调度者马上安排一个工作线程工作。

12    已知的应用
1.    CORBA ORBS
2.    ACE Framework
3.    Siemens MedCom
4.    Siemens Call Center management system:
5.    Actors
译者:此节只列出他们的名字,感兴趣的同志请参考原文。和看《设计模式》的时候情况一样,我不太关注这一节。
13    后果
有下面的好处:
1.    增强了程序的并行性,降低了同步的复杂性。客户线程和异步调起操作并行执行。同步的复杂性由调度者独立处理。
2.    让多个耗时的操作并行执行。只要软件和硬件支持,可以让多个活动的对象彼此不干扰地同时运行。
3.    方法的执行和调用的顺序可以不一致。方法的调用是异步调用。而方法的执行决定于如何调度。

当然,主动对象也有以下负面的影响
1.    性能过多消耗:系统消耗的程度决定于调度者的实现。用户态和系统态的上下文切换,同步信号的时候,数据的传送都会带来消耗。一般说来,主动对象模式适合大粒度的对象,对很小的对象使用这个模式容易带来性能的过度消耗。请和其它并发模式比较如监控者模式。
2.    增加调试的难度:并发的复杂和调度的不可预测,会增加调试的困难。并且,许多调试工具都不能完全的支持并发程序的调试。

14    更多相关模式
译者:这些模式许多我还也没有接触,准备逐个学习,高兴的话还会翻译
    监控者(Monitor)模式使用后,无论多少线程对一个被动对象调用,保证同时只有一个在实际执行。因为更少的上下文切换和数据传递,这个模式比主动对象效率告。但此模式较难把客户和服务线程分布在不同机器上。
    反应堆(Reactor)模式,当不会再发生阻塞的时候,触发多个事件处理器,分解和触发任务。在存在回调机制的被动对象时,常用这个模式代替主动对象。也常常用它来连接主动对象和下面的半同步半异步模式一起使用。
    半同步半异步(Half-Sync/Half-Async)模式,这个摸索用来分离同步和异步调用。这个模式常常使用活动对象来实现异步任务层。
    命令处理器(Command Processor)模式,这个模式和主动对象差不多。它用来分离请求的发出和执行,一个命令处理器就相当于一个调度者。然而,他没有代理者,客户直接发布命令。
    Broker模式,这个也和主动对象类似。主要的不同是,代理者和执行者是分布边界而不是线程边界。
    互斥体(Mutex)模式。有时代替主动对象模式,简单的在一个主动对象上加一个锁,使其可以并发的被调用。他有多种实现方式,如重叠的锁,支持权限继承的锁。
分享到:
评论

相关推荐

    ActiveObject

    多线程设计模式-activeObject代码

    ASP.NET设计模式-杨明军译(源码)

    《asp.net设计模式》涵盖了开发企业级asp.net应用程序的知名模式和最佳实践。本书用到的模式可以用于从asp.net1.0到asp.net 4.0的任何版本。不必管模式本身所用的语言,可以将模式用于任何面向对象编程语言。  ...

    java8源码-GraphicMultiThreadDesignPattern:图解Java多线程设计模式

    +---activeObject---------------------------主动对象模式 +---balking--------------------------------停止返回模式 +---future---------------------------------等待返回模式 +---builder---------------------...

    java多线程设计模式详解(PDF及源码)

    Phase Termination——快把玩具收拾好,去睡觉吧 第11章 Thread-Specific Storage——每个线程的保管箱 第12章 Active Object——接受异步消息的主动对象 总结 多线程程序设计的模式语言 附录A 练习问题的解答 附录B...

    java多线程设计模式 (PDF中文版, 附源码)

    第12章 Active Object——接受异步消息的主动对象 总结 多线程程序设计的模式语言 附录A 练习问题的解答 附录B Java的内存模型 附录C Java线程的优先级 附录D 线程相关的主要API 附录E 参考文献

    敏捷软件开发原则、模式与实践 C#版

    Martin深入而生动地 使用真实案例讲解了面向对象设计的基本原则、重要的设计模式、UML和敏 捷方法。 本书Java版曾荣获2003年第13届Jolt大奖,是公认的经典著作。本书是 C#程序员提升功力的绝佳教程,也可用作高校...

    敏捷软件开发:原则、模式与实践

    第13章 COMMAND模式和ACTIVE OBJECT模式 第14章 TEMPLATE METHOD模式和STRATEGY模式:继承与委托 第15章 FACADE模式和MEDIATOR模式 第16章 SINGLETON模式和MONOSTATE模式 第17章 NULL OBJECT模式 第18章 薪水支付...

    ACE技术论文集(已翻译为中文)

    第6章主动对象(Active Object):用于并发编程的对象行为模式 第 7 章 ACE 反应器(Reactor)的设计和使用:用于事件多路分离的面向对象框架 第 8 章 前摄器(Proactor):用于为异步事件多路分离和分派处理器的...

    敏捷软件开发:原则、模式与实践.pdf

    1994年,Martin的第一本著作《Designing Object-Oriented C++ Application Using the Booch Method》也由 Prentice Hal l出品。这两本书彼此相互辉映,当时引起了很大的反响。Martin 的这本书结合了当时最流行的面向...

    敏捷软件开发:原则、模式与实践.pdf

    第十三章 COMMAND模式和ACTIVE OBJECT模式 第十四章 TEMPLATE METHOD模式和STRATEGY模式:继承与委托 第十五章 FACADE模式和MEDIATOR模式 第十六章 SINGLETON模式和MONOSTATE模式 第十七章 NULL OBJECT模式 第十八章...

    Visual_Studio.NET相关词汇中英翻译

    active object 活动对象 active point 活动点 Active Template Library 活动模板库 ActiveX Component ActiveX 组件 ActiveX Control ActiveX 控件 ActiveX control container ActiveX 控件容器 ActiveX Control ...

    qpc :一个开源的状态机实现

    QP 框架系列基于 Active Object (actor) 设计模式,它本质上支持并自动强制执行 以下并发编程的最佳实践: 保持数据隔离并绑定到活动对象的线程。线程应该 隐藏(封装)他们的私人数据和其他资源,而不是 与系统...

    敏捷软件开发.pdf

    28.1 VISITOR设计模式系列 28.2 VISITOR模式 28.3 ACYCLIC VISITOR模式 28.4 DECORATOR模式 28.5 EXTENSION OBJECT模式 28.6 结论 参考文献 第29章 STATE模式 29.1 有限状态自动机概述 29.2 实现技术 ...

    敏捷软件开发:原则、模式与实践.pdf 高清

    还重点讲述了如何使用UML和设计模式解决面向客户系统的问题。本书于2003年荣获第13届软件开发图书震撼大奖,适于用作高校计算机专业本科生、研究生和软件学院的软件工程和软件开发相关课程的教材或参考书,也适于...

    qpn:QP-nano实时嵌入式框架针对基于活动对象(角色)和分层状态机的嵌入式系统的RTOS

    框架系列基于 ( actor )设计模式,该模式固有地支持并自动实施以下并行编程最佳实践: 使数据隔离并绑定到活动对象的线程。 线程应该隐藏(封装)它们的私有数据和其他资源,并且不要与系统的其余部分共享它们。 ...

    ASP3《高级编程》(第一部分)

    3.3 ASP的Application对象和Session对象 83 3.3.1 ASP的Application对象成员概述 84 3.3.2 ASP的Session对象成员概述 85 3.3.3 使用Application和Session的事件 86 3.3.4 活动中的ASP Application对象 89 3.3.5...

    ASP3《高级编程》(第二部分)

    3.3 ASP的Application对象和Session对象 83 3.3.1 ASP的Application对象成员概述 84 3.3.2 ASP的Session对象成员概述 85 3.3.3 使用Application和Session的事件 86 3.3.4 活动中的ASP Application对象 89 3.3.5...

Global site tag (gtag.js) - Google Analytics