反應堆Reactor模式


原文:http://blog.chinaunix.net/uid-20528042-id-1935072.html

翻譯國外講解Reactor的實現方式。

反應堆

對象行為類的設計模式,對同步事件分揀和派發。


1.意圖

應用中,用反應堆模式處理並發的請求,這些請求可能來自多個客戶端。應用提供多個服務,每個服務包含多個事件處理方法。一個服務就是一個獨立的事件處理器,一個提供特定Service的Handler。事件到來時,使用Dispatcher(分發器)對Handler進行分派,這個Dispatcher要對所有注冊的Handler進行維護。同時,有一個Demultiplexer(分揀器)對多路的同步事件進行分揀。



2.別名

Dispatcher(分發器),Notifer(通知器)


3.例子

為了說明反應堆模式,考慮一個例子:一個事件驅動的服務程序,這個程序在分布環境中運行,提供網絡日志服務。如圖1。客戶端使用日志服務記錄自己的運行情況,如:錯誤,跟蹤調試信息,和性能報告。所有的日志記錄送到日志服務器,日志服務可以把信息寫入各種輸出設備,如終端界面,打印機,文件,或數據庫。

圖1中,日志服務程序處理日志記錄和客戶端的連接請求,一個Handle對應一個網絡連接,網絡連接是被操作系統管理的資源,如套接字,不同的Handle上日志記錄和服務請求會並發出現。


日志服務端和客戶端使用面向連接的網絡協議,如TCP[1]。在客戶端發送日志以前必須先建立連接。服務端使用一個Handle Factory監聽網絡端口,等待客戶端的連接請求。當連接請求達到,Handle Factory建立與客戶端的連接,並創建一個新的Socket Handle代表這個連接。連接建立后,客戶端就可以並發地向服務端發送日志記錄。日志服務通過Socket Handle來接收日志記錄。

開發並發的日志服務,最容易相當的方式就是多線程,使用多線程處理多個客戶端的連接,如圖2。這種方式下,為每個客戶端連接創建一個獨立的線程進行處理。

 

但是,多線程的方式不能滿足下面的要求:

性能:因為上下文切換,異步和數據遷移的消耗,多線程可能導致低效率。

編程實現的簡單性:多線程要求復雜的同步控制機制。

可移植性:並不是所有的操作系統都支持多線程

因為這些缺陷,多線程常常不是最高效的方案,更不是最容易實現的方案。


4.環境

在一個分布式系統中,一個服務程序並發的接收和處理多個客戶端的事件。


5.問題

服務程序必須處理多個客戶端的請求。服務程序必須對到來的請求分發給正確的服務提供者。開發一個高效率的服務機構,完成分揀和派發,必須要考慮下面的需要:

可用行:服務在等待其它請求的同時處理新到來的請求。特別地,服務不能在處理請求的時候陷入無限阻塞。

效率:快速反應,高吞吐量。

編程實現簡單:簡化同步控制的復雜性。

適應性:當增加新的服務或者改進原有的服務,有盡量小的修改和維護成本。比如,添加一個新的服務,不需要改變分揀和分派機構。

可移植性:容易移植到新的操作系統。


6.方案

集成異步事件的分揀和派發。構造與具體應用無關的,通用的框架。

應用程序提供的每種服務對應一個獨立的Event Handler,每種Handler處理一種類型的事件。所有的Event Handler有接口。這樣,不同的Event Handler會注冊到Initiation Dispacher, Initiation Dispatcher使用Synchronous Event Demultiplexer等待事件的發生。事件發生時,Demultiplexer通知Dispacher,Dispatcher回調Handler,Handler調用對應的事件處理方法。


7.結構

這個模式中,有下面幾個關鍵的參與者


Handle

Handle代表操作系統管理的資源,包括:網絡鏈接,打開的文件,計時器,同步對象等等。在我們的日志服務中,Handle代表與客戶端連接的套接字,Synchronous Event Demultiplexer在這些套接字上等待事件的發生。日志服務主要關注兩種類型的事件,connection事件和read事件,分別對應客戶連接請求和日志記錄請求。日志服務為每一個客戶端保持一個連接。每個連接就對應了一個socket handle。


Synchronous Event Demultiplexer

在一個Handle集合上等待事件的發生。這里常用系統調用select[1],UNIX和WIN32平台都支持這個系統調用。select的返回結果說明handle上發生情況,需要被處理。


Initiation Dispatcher

提供接口:注冊,刪除和派發Event Handler。上面的Synchronous Event Demultiplexer等待事件的發生,當檢測到新的事件,就把事件交給Initiation Dispatcher,它去回調Event Handler。事件種類一般有:接受到連接,數據輸入,數據輸出,超時。


Event Handler

定義一個抽象接口,包含一個鈎子方法,實現特定服務的派發操作。這個方法實現了與特定應用相關的服務。


Concrete Event Handler

繼承上面的類,實現鈎子方法。應用把Concrete Event Handler注冊到Initiation Dispatcher,等待被處理的事件。當事件發生,這些方法被回調。

在日志服務中,共有兩種Concrete Event Handler , Logging Handler和Logging Acceptor。分別負責接受日志記錄和新的客戶端連接。


參考下面的類圖

8.動態

8.1 概要協作過程

反應堆模式中,發生下面的協作過程

*當注冊一個Concrete Event Handler到Initiation Dispatcher,要告知這個Handler感興趣的事件類型。

*Initiation Dispatcher要求每一個Event handler傳遞其內部的Handle。

*當Event Handler注冊完畢,進入Initiation Dispatcher的事件循環。Initiation Dispatcher 把所有Event Handler中的Handle組合在一起,使用Synchronous Demultiplexer 去等待事件的發生。例如,使用select調用等待TCP協議的socket事件。

*當事件源准備好,例如,TCP socket可以被讀了。Synchronous Demultiplexer通知Initiation Dispatcher。

*Initiation Dispatcher觸發Event Handler的鈎子方法。Initiation Dispatcher是通過handle定位到Event Handler並調用其方法的。

*Event Handler的hook方法被調用完成事件的處理。

參看下圖


8.2 協作的場景

在日志服務,協作的過程可以分為兩個場景。分別說明了日志服務如何處理客戶端的連接和日志記錄請求。


8.2.1 客戶端連接到日志服務

在此場景中,客戶端連接到日志服務,如下圖。


總結為下面的步驟。

1. 注冊Logging Acceptor到Initiation Dispatcher,等待處理連接請求;

2. Initiation Dispatcher的handle_events方法被調用;

3. Initiation Dispatcher調用Synchronous Demultiplexer的select方法,等待連接請求或日志數據的到達;

4. 一個客戶端連接到日志服務器;

5. Initiation Dispatcher通知Loging Acceptor:有新的連接請求到達;

6. Logging Acceptor接受新的請求;

7. Logging Acceptor創建一個新的Logging handler去為新的客戶服務;

8. Logging Handler把handle注冊到Initiation Dispatcher。


8.2.2 客戶端發送日志記錄到日志服務如下圖

有下面的步驟

1. 客戶端發送日志記錄;

2. 當客戶的日志記錄數據到達套接字,Initiation Dispatcher通知Logging Handler;

3. 接受日志數據;

4. 處理日志數據;

5. 返回到Initiation Dispatcher的處理循環。


9 實現

這一節說明如何使用C++實現反應堆模式,這個實現參照ACE框架[2]的實現。


9.1 同步分揀機制

Initiation Dispatcher 利用一個Synchronous Demultiplexer等待一個或多個事件的發生。一般通過系統調用select實現。select用來檢查哪些Handle上已經准備好進行I/O操作。一般情況下,Synchronous Demultiplexer都是使用操作系統提供的功能實現的。


9.2 開發一個Initiation Dispatcher

Event Handler表 :Initiation Disapatcher要維護一個表,表中保存所有的Event handler。表中的handler可以在運行時添加和刪除。這個數據結構有多種實現方式,哈希表,線性表,如果代表handles的是一個較小范圍的整數,還可以使用直接索引的方法。

事件處理循環的入口點:使用handle_events方法提供。這個方法控制Handle的分揀和Event handler的分派。一般情況下,事件處理循環就是整個應用程序的主循環。

事件發生,Select調用返回,Initiation Dispatcher被激活,回調Event Handler的事件處理,事件處理完畢,又返回到Initiatin Disaptcher的處理中。


參看下面的C++代碼
enum Event_Type
// = TITLE
// Types of events handled by the
// Initiation_Dispatcher.
//
// = DESCRIPTION
// These values are powers of two so
// their bits can be efficiently ‘‘or’d’’
// together to form composite values.
{
ACCEPT_EVENT = 01,
READ_EVENT = 02,
WRITE_EVENT = 04,
TIMEOUT_EVENT = 010,
SIGNAL_EVENT = 020,
CLOSE_EVENT = 040
};
class Initiation_Dispatcher
// = TITLE
// Demultiplex and dispatch Event_Handlers
// in response to client requests.
{
public:
// Register an Event_Handler of a particular
// Event_Type (e.g., READ_EVENT, ACCEPT_EVENT,
// etc.).
int register_handler (Event_Handler *eh,
Event_Type et);

// Remove an Event_Handler of a particular
// Event_Type.
int remove_handler (Event_Handler *eh,
Event_Type et);

// Entry point into the reactive event loop.
int handle_events (Time_Value *timeout = 0);
};

必要的同步機制: 如果反應堆模式使用單線程實現,可以不必考慮任何同步問題。

然而,在一個多線程的環境里,也可以使用Initiation Disaptcher作為一個事件分發中心。這時,在訪問共享數據的時候,要注意同步。可以考慮使用信號量或互斥體。

為了防止死鎖發生,可以考慮使用recursive locks[4](這個需要專門研究)。


9.3 派發目標的類型:

一共有兩種類型的Event handler,可以選擇其中之一,或兩個都使用。

Event Handler對象。讓Handler成為一共對象是常用的方法。在第7節的例子中,Event Handler的子類的對象被注冊到Initiation Dispatcher。使用這種方式,很方便重用和擴展。

Event Handler函數。另外一種方式,注冊函數到Initiation Dispatcher。增加一個處理的時候,注冊回調函數,不需要增加新類。

使用Adaptor模式[5],可以同時實現兩種方式。例如,一個專用的adapter用來封裝回調的函數的指針,當這個adapter的handle_event被調用,指針指向的函數被調用。


9.4 定義事件處理的接口

假設我們使用Event Handler對象,下一步是定義它的接口。

單方法的接口:參看第7節的類圖,Event handler基類包含一個唯一的接口函數:handle_event,這個函數被Initiation Dispatcher用來分發事件。在這個例子中,事件的類型作為事件處理方法的參數。

下面是C++的代碼,定義一個方法接口的抽象基類。

class Event_Handler
// = TITLE
// Abstract base class that serves as the
// target of the Initiation_Dispatcher.
{
public:
// Hook method that is called back by the
// Initiation_Dispatcher to handle events.
virtual int handle_event (Event_Type et) = 0;
// Hook method that returns the underlying
// I/O Handle.
virtual Handle get_handle (void) const = 0;
};

單方法接口的優點,是增加新的事件處理類型,不需要改變接口。然而,使用這種方法,處理的時候要使用switch語句,這會影響代碼的可擴展性。

多方法接口:為每一種事件類型定義一種方法。

參看下面的C++代碼

class Event_Handler
{
public:
// Hook methods that are called back by
// the Initiation_Dispatcher to handle
// particular types of events.
virtual int handle_accept (void) = 0;
virtual int handle_input (void) = 0;
virtual int handle_output (void) = 0;
virtual int handle_timeout (void) = 0;
virtual int handle_close (void) = 0;
// Hook method that returns the underlying
// I/O Handle.
virtual Handle get_handle (void) const = 0;
};

多方法接口的優點:子類重定義基類的函數,避免近一步判斷分別處理。但這種方法需要框架的開發人員預先知道所有的事件處理類型。在我們的例子中,所有的handle_*函數,都可以被UNIX的select系統調用激活。然而,這個接口不能涵蓋所有Win32 WaitForMultipleObject機構包含的類型。

9.5 確定Initiation Disaptcher的個數。

大多數反應堆模式的應用,只需要一個反應堆。這種情況下,可以使用單件模式[5]。這種方式利於集中在一個地方處理事件的分揀和派發。

然而一些操作系統堆單個線程能夠等待處理的對象進行了限制。例如,在一個線程中,Win32的select和WaitForMultipleObject最多能等待64個handle。這種情況下,必須數據多線程,每一個線程運行自己的反應堆。

多線程環境下,多個Event Handler可能同時運行,可能需要有同步機制。


9.6 實現Event handler

下面的代碼實現了日志服務的例子,處理連接的Logging Acceptor和接收日志數據的data

reception。

Logging Accesptor 類:這個例子來自Acceptor-Connector 模式[8]。這個模式分

離了服務的實現和服務的激活。

Logging Acceptor 被動的接收來自客戶端的連接,關鍵的方法和數據結構,如下面的代碼

class Logging_Acceptor : public Event_Handler
// = TITLE
// Handles client connection requests.
{
public:
// Initialize the acceptor_ endpoint and
// register with the Initiation Dispatcher.
Logging_Acceptor (const INET_Addr &addr);

// Factory method that accepts a new
// SOCK_Stream connection and creates a
// Logging_Handler object to handle logging
// records sent using the connection.
virtual void handle_event (Event_Type et);

// Get the I/O Handle (called by the
// Initiation Dispatcher when
// Logging_Acceptor is registered).
virtual HANDLE get_handle (void) const
{
return acceptor_.get_handle ();
}
private:
// Socket factory that accepts client
// connections.
SOCK_Acceptor acceptor_;
};

這個類繼承於Logging_Acceptor基類,所以可以被注冊到Initiation Dispatcher。

Logging Acceptor包含一個SOCK Acceptor對象,這是一個工廠,通過這個工廠接收到新的連接請求,工廠內部維護一個監聽的套接字。當連接到達,SOCK Acceptor 接收連接並產生一個新的SOCK Stream。這個SOCK Stream代表新的客戶端連接,用來傳輸日志數據。

SOCK Acceptor和SOCK Stream其實都是ACE[9]提供的工具類。這些包裝類封裝了套接字接口的細節,提供了統用的面向對象的接口。

Logging Acceptor的構造函數注冊自己。

Logging_Acceptor::Logging_Acceptor
(const INET_Addr &addr)
: acceptor_ (addr)
{
// Register acceptor with the Initiation
// Dispatcher, which "double dispatches"
// the Logging_Acceptor::get_handle() method
// to obtain the HANDLE.
Initiation_Dispatcher::instance ()->register_handler (this, ACCEPT_EVENT);
}

當事件到達,handle_event函數被調用

void Logging_Acceptor::handle_event (Event_Type et)
{
// Can only be called for an ACCEPT event.
assert (et == ACCEPT_EVENT);
SOCK_Stream new_connection;

// Accept the connection.
acceptor_.accept (new_connection);

// Create a new Logging Handler.
Logging_Handler *handler =
new Logging_Handler (new_connection);
}

這個函數調用accept函數被動的接收新的連接SOCK Stream,然后動態創建一個Logging Handler用來處理這個連接的上日志記錄請求。如下面的說明,Logging handler也會把自己注冊到Initiation Distpatcher。


Logging Handler類:這個類接收客戶端發來的日志記錄。
class Logging_Handler : public Event_Handler
// = TITLE
// Receive and process logging records
// sent by a client application.
{
public:
// Initialize the client stream.
Logging_Handler (SOCK_Stream &cs);

// Hook method that handles the reception
// of logging records from clients.
virtual void handle_event (Event_Type et);

// Get the I/O Handle (called by the
// Initiation Dispatcher when
// Logging_Handler is registered).

virtual HANDLE get_handle (void) const
{
return peer_stream_.get_handle ();
}

private:
// Receives logging records from a client.
SOCK_Stream peer_stream_;
};

這個類同樣繼承與Event_Handler,可以注冊到Initiation Dispatcher. 如下說明:

Logging_Handler::Logging_Handler
(SOCK_Stream &cs)
: peer_stream_ (cs)
{
// Register with the dispatcher for
// READ events.
Initiation_Dispatcher::instance ()->
register_handler (this, READ_EVENT);
}

當日志數據到達,下面的函數被調用

void Logging_Handler::handle_event (Event_Type et)
{
if (et == READ_EVENT) {
Log_Record log_record;
peer_stream_.recv ((void *) log_record, sizeof log_record);

// Write logging record to standard output.
log_record.write (STDOUT);
}
else if (et == CLOSE_EVENT) {
peer_stream_.close ();
delete (void *) this;
}
}

和上面的處理類似,當客戶端關閉了一個連接,會觸發COLSE事件,這個事件被上面的函數同樣處理。


9.7 實現服務

整個應用程序包含一個唯一的主程序。

日志服務程序的主函數:實現單線程的,並發的日志處理。事件處理循環在Initiation Dispatcher的handle_events函數里面。當用戶的請求到達,對應的Event Handler的hook方法被調用,處理連接請求和日志記錄請求。主函數代碼如下:

// Server port number.
const u_short PORT = 10000;
int main (void)
{
// Logging server port number.
INET_Addr server_addr (PORT);

// Initialize logging server endpoint and
// register with the Initiation_Dispatcher.
Logging_Acceptor la (server_addr);

// Main event loop that handles client
// logging records and connection requests.
for (;;)
Initiation_Dispatcher::instance ()->
handle_events ();
/* NOTREACHED */
return 0;
}

主函數創建 Logging Acceptor,它的構造函數會初始化並監聽指定的端口。然后程序就進入了事件處理循環,在單例對象Initiation Dispatcher中handle_events會等待並處理連接和日志記錄請求。

下面的交互圖展示了協作的過程


一旦Initiation Dispatcher被實例化,這個單例的對象就成為控制的中心,在其控制下,注冊其上的Handler的hook方法被觸發。

連接請求到達,Initiation Dispatcher回調Logging Acceptor,Logging Acceptor創建一個Logging Handler並注冊到到Initiation Dispatcher。然后,客戶發送日志數據,Initiation Dispatcher回調相應的Logging Handler處理日志記錄,所有這些發生在一個單線程中。


10 已知的應用

反應堆模式,運行在許多面向對象的框架中。

InterViews: The Reactor pattern is implemented by the InterViews [10] window system distribution, where it is known as the Dispatcher. The InterViews Dispatcher is used to define an application’s main event loop and to manage connections to one or more physicalGUI displays.

ACE Framework: The ACE framework [11] uses the Reactor pattern as its central event demultiplexer and dispatcher.

反應堆模式在許多商業項目中使用。

CORBA ORBs: The ORB Core layer in many singlethreaded implementations of CORBA [12] (such as VisiBroker,Orbix, and TAO [13]) use the Reactor pattern to demultiplex and dispatch ORB requests to servants.

Ericsson EOS Call CenterManagement System: This system uses the Reactor pattern to manage events routed by Event Servers [14] between PBXs and supervisors in a Call Center Management system.

Project Spectrum: The high-speed medical image transfer subsystem of project Spectrum [15] uses the Reactor pattern in a medical imaging system.


11 后果

11.1 好處

使用反應堆模式,有下面的好處。

分割並征服:分離與應用無關的分發和與應用相關的處理。分發相關的組件成為可重用的組件

提高了事件驅動應用的模塊化程度,可重用性和可配置性。不同的功能使用不同的類實現,例如,建立連接和接收處理數據使用不同的類。這樣分離實現后,不同類型的面向連接的服務(如文件傳輸,遠程登錄或VOD系統)就可以重用建立連接的類。並且,當需要修改或擴展功能的時候,只需要修改Loggning handler類。

提高了程序的可移植性:Initiation Dispatcher的接口定義是與操作系統接口無關的,可以在不同的平台上實現這些接口。

提供了粗粒度的並發控制,使用單線程實現,避免了復雜的同步處理。

 
11.2 壞處

使用反應堆模式,導致下面的負作用。

應用受限制: 反應堆模式只能應用在支持Handle的操作系統上。雖然,可一使用多線程模擬反應堆,但是,因為同步控制和上下文切換的要求,這種實現效率低,與反應堆模式的出發點相違背。

非搶占模式:在單線程的實現,這種情況下,事件的處理必須不能使用阻塞的I/O,因此,如果存在長期操作,比如傳輸大量的數據。使用主動對象,效率可能更好。主動對象可以並發的處理這些任務。

難以調試

使用反應堆模式的應用程序可能會難以調試,因為程序運行的控制流會在框架和應用相關的處理器之間跳轉。不了解框架的應用程序開發人員難一跟着調試。這就相調試一個詞法或語法分析器生成的代碼。這樣的程序中,控制線程在用戶定義行為的內部。當代碼執行到有限自動機的內部。會難以跟蹤程序的邏輯。


12 其他

和觀察者模式(Observer)[5]相關,當一個對象改變,其他多個模塊被通知。在反應堆模式中,當某個Handle感有興趣的事件發生,Handler被通知。反應堆模式用來分揀多個事件源的事件,而觀察者模式常常只關聯一個事件源。

和責任鏈模式相關(Chain of Responsibility)[5],當把一個請求委托給服務提供者。反應堆模式與責任鏈不同,反應堆模式關聯一個特定的事件處理器到特定的事件源,而責任鏈模式搜索一個序列去定義第一個匹配的事件處理器。

反應堆模式還被認為是異步Proactor[18]模式的同步變種,Proactor支持異步事件源的事件分揀和多個事件處理器的分派。不同的時,反應堆模式支持的是沒有阻塞的同步事件源。

主動對象模式[17]分離了方法的執行和調用,簡化了多個線程調用時同步控制的復雜性(這個模式我也翻譯過了),。當多線程不可用,或者任務使用多線程太復雜了,反應堆模式經常替換主動對象。

在實現反應堆模式的時候,可以提供一個外觀(Facade)[5]類處理事件的分揀。一個外觀類用來隱藏一個子系統中復雜的對象關系


參考

[1] W. R. Stevens, UNIX Network Programming, First Edition. Englewood Cliffs, NJ: Prentice Hall, 1990.

[2] D. C. Schmidt, “ACE: an Object-Oriented Framework for Developing Distributed Applications,” in Proceedings of the 6th USENIX C++ Technical Conference, (Cambridge, Massachusetts), USENIX Association, April 1994.

[3] W. Pree, Design Patterns for Object-Oriented Software Development. Reading, MA: Addison-Wesley, 1994.

[4] D. C. Schmidt, “An OO Encapsulation of Lightweight OS Concurrency Mechanisms in the ACE Toolkit,” Tech. Rep. WUCS-95-31, Washington University, St. Louis, September 1995.

[5] E. Gamma, R. Helm, R. Johnson, and J. Vlissides, Design Patterns: Elements of Reusable Object-Oriented Software. Reading, MA: Addison-Wesley, 1995.

[6] D. C. Schmidt and P. Stephenson, “Experiences Using Design Patterns to Evolve System Software Across Diverse OS Platforms,” in Proceedings of the 9th European Conference on Object-Oriented Programming, (Aarhus, Denmark), ACM, August 1995.

[7] S. Berczuk, “A Pattern for Separating Assembly and Processing,” in Pattern Languages of Program Design (J. O. Coplien and D. C. Schmidt, eds.), Reading, MA: Addison-Wesley, 1995.

[8] D. C. Schmidt, “Acceptor and Connector: Design Patterns for Initializing Communication Services,” in Pattern Languages of Program Design (R. Martin, F. Buschmann, and D. Riehle, eds.), Reading, MA: Addison-Wesley, 1997.

[9] I. Pyarali, T. H. Harrison, and D. C. Schmidt, “Design and Performance of an Object-Oriented Framework for High-Performance Electronic Medical Imaging,” in Proceedings of the 2nd Conference on Object-Oriented Technologies and Systems, (Toronto, Canada), USENIX, June 1996.

[10] M. A. Linton and P. R. Calder, “The Design and Implementation of InterViews,” in Proceedings of the USENIX C++ Workshop, November 1987.

[11] D. C. Schmidt, “The ACE Framework.” Available from www.cs.wustl.edu/schmidt/ACE.html, 1997.

[12] Object Management Group, The Common Object Request Broker: Architecture and Specification, 2.0 ed., July 1995.

[13] D. C. Schmidt, D. L. Levine, and S. Mungee, “The Design and Performance of Real-Time Object Request Brokers,” Computer Communications, vol. 21, pp. 294324, Apr. 1998.

[14] D. C. Schmidt and T. Suda, “An Object-Oriented Framework for Dynamically Configuring Extensible Distributed Communication Systems,” IEE/BCS Distributed Systems Engineering Journal (Special Issue on Configurable Distributed Systems), vol. 2, pp. 280293, December 1994.

[15] I. Pyarali, T. H. Harrison, and D. C. Schmidt, “Design and Performance of an Object-Oriented Framework for High-Performance Electronic Medical Imaging,” USENIX Computing Systems, vol. 9, November/December 1996.g

[16] H. Custer, Inside Windows NT. Redmond, Washington: Microsoft Press, 1993.

[17] R. G. Lavender and D. C. Schmidt, “Active Object: an Object Behavioral Pattern for Concurrent Programming,” in Proceedings of the 2nd Annual Conference on the Pattern Languages of Programs, (Monticello, Illinois), pp. 17, September 1995.

[18] T. Harrison, I. Pyarali, D. C. Schmidt, and T. Jordan, “Proactor  An Object Behavioral Pattern for Dispatching Asynchronous

Event Handlers,” in The 4th Pattern Languages of Programming Conference (Washington University technical report #WUCS-97-34), September 1997.



注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com