譯者注:
1.對象分爲主動對象和被動對象,主動對象內部包含一個線程,能夠自動完成動做或改變狀態,而通常的被動對象只能經過被其餘對象調用纔有所做爲。在多線程程序中,常常把一個線程封裝到主動對象裏面。
2.在翻譯過程當中,發現的原文不妥處被更正。
3.原文中許多內容一再重複,很有蛇足之感,取精用宏,刪繁就簡。
4.嘗試更高程度的意譯。設計模式
關鍵詞
這個文章介紹主動對象模式,主動對象是內部擁有本身的控制線程的對象。爲了簡化異步調用的複雜性,這個模式分離了方法的執行和調用。使用這個模式,一個對象中不管是否有獨立的線程,客戶從外部訪問它時,感受是同樣的。生產者/消費者,讀者/寫者,這兩個應用普遍的模型中,這種策略很是適合。這個模式經常使用在多線程的,分佈式系統中。另外,一些客戶端應用,如:視窗系統和網絡瀏覽器,也可使用主動對象模式,對並行的、異步調起的IO操做簡化處理。數組
1 目的瀏覽器
主動對象模式隔離了方法執行和方法調用的過程,提升了並行性,對內部擁有控制線程的主動對象,下降了異步訪問的複雜性。緩存
2 別名安全
並行行爲對象(ConcurrentObjectandActor)網絡
3 例子數據結構
爲了說明主動對象模式,考慮一個通訊網關的設計。網關隔離互相協做的多個組成單元,讓交互過程不直接依賴於對方。參照圖1,一個分佈式系統中,來自多個生產者(衛星設備)的消息,被網管轉發給多個消費者(局域網內的主機)。多線程
咱們的例子中,生產者和消費者使用TCP協議通訊,這是面向鏈接的通訊協議。網關上的進程向消費者發送數據時,操做會發生阻塞。這是由於,網絡傳輸能力有限,TCP進行流量控制,防止過量的數據不能及時緩衝和處理。併發
要提升總體的效率,網關處理進程不能因一個鏈接上的阻塞而等待。另外,當生產者和消費者的數目增長的時候,整個進程必須相應的增長處理能力(譯者注:經過增長處理線程)。app
一個提升性能的有效方法,就是使用並行。應用並行後,服務對象擁有獨立線程,線程實際完成操做,和方法調用的過程分開。而且,不一樣線程處理不一樣TCP鏈接,一個鏈接上的線程被阻塞,不會影響到其餘鏈接的線程。
4 場景
對象的訪問者(Client,下統稱爲客戶)和對象的實如今不一樣的線程中。
5 問題
許多應用場景,服務者並行處理多客戶端的請求,提升服務的質量(QoS)。被動對象在客戶線程中完成操做過程,主動對象使用專用的線程完成。一個對象的數據被多個線程共享時,必須處理好線程的同步。這致使三個約束。
1.對一個對象調用不能阻塞整個進程,不能影響其餘線程的執行:好比,通訊網關的例子中,一個TCP鏈接上的數據傳送被阻塞,整個進程仍然能繼續處理。一樣,其餘沒有被阻塞的網絡鏈接,應該正常的發送數據。
2.對共享對象同步訪問的邏輯應該簡化:客戶在使用共享對象地時候,若是面對底層的同步機制,必須記得先要獲取互斥鎖,後要釋放互斥鎖,編碼就比較麻煩。通常狀況,共享對象的方法隱藏這些細節,當多個客戶線程訪問同一個對象時,這些約束是透明的。
3.存在能夠平行進行的操做,設計成同時執行:在通訊網關的例子中,同時使用多個TCP鏈接給不一樣的消費者發送數據。若是網關的進程在單個線程上執行,及時使用多個處理器不能明顯的提升性能。
6 方案
對每個要求併發執行的對象,分離其方法的調用和執行。這樣,這個對象的客戶就像是調用一個常規的方法同樣。這個方法,自動把任務交給另外的線程完成執行。
主動對象的組成:一個代理者(Proxy)實現外部的訪問接口;一個執行者(Servant)。代理和執行者在不一樣的線程執行,分離方法調用和執行的過程:代理者在客戶線程中被調用執行,執行者在另外的線程完成操做。運行時,代理者把客戶的調用信息封裝「調用請求」(MethodRequest),經過調度者(Scheduler)把這個請求放到一個活動隊列(ActivationQueue)。調度者和執行者運行在另外的線程中,這個線程啓動後,不斷地從活動隊列中獲得「調用請求」對象,派發給執行者完成客戶請求的操做。客戶調用代理者後立刻獲得一個預定容器(Future),從此能夠經過這個預定容器獲得返回的結果。
7 結構
下面使用Booch風格的類圖,對主動對象的組成結構進行說明。(譯者注:這是Booch在《面向對象的分析和設計》書中使用的類圖風格)
在這個模式中,共有六個參與者。
代理者(Proxy)
代理者定義了被客戶調用的接口。這個接口是函數調用的方式,而不是像傳統線程通訊,使用數據傳遞的方式。當函數被調用的,代理者構造一個「調用請求」對象,並把它放到活動隊列中,這一切都發生在客戶線程中。
調用請求(MethodRequest)
「調用請求」用來保存相關函數調用的部上下文信息,好比函數標識,函數的參數,這些信息將在不一樣線程間傳遞。一個抽象的「調用請求」類,定義了執行活動對象方法的接口Call。而且包含一個Guard函數,Guard用來檢查調用的條件是否知足。對代理者提供的每個主動對象方法,在訪問其執行者的時候須要條件判斷。代理者被調用的時候會建立具體「調用請求」對象,對象中包含了執行這個方法必須的參數和數據的返回方式。
活動隊列(ActivationQueue)
這個隊列維護了一個緩衝區(譯者注:不必定是先進先出的隊列),緩衝區中存放了待執行的「調用請求」。正是這個隊列分離可客戶線程和執行操做的線程。
調度者(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.執行者的實現:咱們的例子中,執行者是個消息隊列,用來緩衝發送到消費者的消息。對每個遠程的消費者,對應一個ConsumerHandler,Handler中包含一個到消費者進程的TCP鏈接(譯者注:每個主動對象要包裝一個ConsumerHandler)。每個ConsumerHandler對應的活動對象緩存的消息(譯者:經過活動隊列緩存「請求調用「的方式緩存消息),是從生產者發給網關的,而且等待網關把它發送給對應的消費者。下面的類定義了執行者的接口。
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); // 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.CORBAORBS
2.ACEFramework
3.SiemensMedCom
4.SiemensCallCentermanagementsystem:
5.Actors
譯者:此節只列出他們的名字,感興趣的同志請參考原文。和看《設計模式》的時候狀況同樣,我不太關注這一節。
13 後果
有下面的好處:
1.加強了程序的並行性,下降了同步的複雜性。客戶線程和異步調起操做並行執行。同步的複雜性由調度者獨立處理。
2.讓多個耗時的操做並行執行。只要軟件和硬件支持,可讓多個活動的對象彼此不干擾地同時運行。
3.方法的執行和調用的順序能夠不一致。方法的調用是異步調用。而方法的執行決定於如何調度。
固然,主動對象也有如下負面的影響
1.性能過多消耗:系統消耗的程度決定於調度者的實現。用戶態和系統態的上下文切換,同步信號的時候,數據的傳送都會帶來消耗。通常說來,主動對象模式適合大粒度的對象,對很小的對象使用這個模式容易帶來性能的過分消耗。請和其它併發模式比較如監控者模式。
2.增長調試的難度:併發的複雜和調度的不可預測,會增長調試的困難。而且,許多調試工具都不能徹底的支持併發程序的調試。
14 更多相關模式
譯者:這些模式許多我還也沒有接觸,準備逐個學習,高興的話還會翻譯
監控者(Monitor)模式使用後,不管多少線程對一個被動對象調用,保證同時只有一個在實際執行。由於更少的上下文切換和數據傳遞,這個模式比主動對象效率告。但此模式較難把客戶和服務線程分佈在不一樣機器上。
反應堆(Reactor)模式,當不會再發生阻塞的時候,觸發多個事件處理器,分解和觸發任務。在存在回調機制的被動對象時,經常使用這個模式代替主動對象。也經常用它來鏈接主動對象和下面的半同步半異步模式一塊兒使用。
半同步半異步(Half-Sync/Half-Async)模式,這個摸索用來分離同步和異步調用。這個模式經常使用活動對象來實現異步任務層。
命令處理器(CommandProcessor)模式,這個模式和主動對象差很少。它用來分離請求的發出和執行,一個命令處理器就至關於一個調度者。然而,他沒有代理者,客戶直接發佈命令。
Broker模式,這個也和主動對象相似。主要的不一樣是,代理者和執行者是分佈邊界而不是線程邊界。
互斥體(Mutex)模式。有時代替主動對象模式,簡單的在一個主動對象上加一個鎖,使其能夠併發的被調用。他有多種實現方式,如重疊的鎖,支持權限繼承的鎖。