ACE主動對象模式(1)

主動對象模式用於降低方法執行和方法調用之間的耦合。該模式描述了另外一種更爲透明的任務間通信方法。

傳統上,所有的對象都是被動的代碼段,對象中的代碼是在對它發出方法調用的線程中執行的,當方法被調用時,調用線程將阻塞,直至調用結束。而主動對象卻不一樣。這些對象具有自己的命令執行線程,主動對象的方法將在自己的執行線程中執行,不會阻塞調用方法。

例如,設想對象"A"已在你的程序的main()函數中被實例化。當你的程序啓動時,OS創建一個線程,以從main()函數開始執行。如果你調用對象A的任何方法,該線程將"流過"那個方法,並執行其中的代碼。一旦執行完成,該線程返回調用該方法的點並繼續它的執行。但是,如果"A"是主動對象,事情就不是這樣了。在這種情況下,主線程不會被主動對象借用。相反,當"A"的方法被調用時,方法的執行發生在主動對象持有的線程中。另一種思考方法:如果調用的是被動對象的方法(常規對象),調用會阻塞(同步的);而另一方面,如果調用的是主動對象的方法,調用不會阻塞(異步的)。

由於主動對象的方法調用不會阻塞,這樣就提高了系統響應速度,在網絡編程中是大有用武之地的。

在這裏我們將一個"Logger"(日誌記錄器)對象對象爲例來介紹如何將一個傳統對象改造爲主動對象,從而提高系統響應速度。

Logger的功能是將一些系統事件的記錄在存儲器上以備查詢,由於Logger使用慢速的I/O系統來記錄發送給它的消息,因此對Logger的操作將會導致系統長時間的等待。

其功能代碼簡化如下:

Cpp代碼  收藏代碼
  1. class Logger: public ACE_Task<ACE_MT_SYNCH>  
  2. {  
  3. public:  
  4.     void LogMsg(const string& msg)  
  5.     {  
  6.         cout<<endl<<msg<<endl;  
  7.         ACE_OS::sleep(2);  
  8.     }  
  9. };  
 

爲了實現實現記錄日誌操作的主動執行,我們需要用命令模式將其封裝,從而使得記錄日誌的方法能在合適的時間和地方主動執行,封裝方式如下:

Cpp代碼  收藏代碼
  1. class LogMsgCmd: public ACE_Method_Object  
  2. {  
  3. public:  
  4.     LogMsgCmd(Logger *plog,const string& msg)  
  5.     {  
  6.         this->log=plog;  
  7.         this->msg=msg;  
  8.     }  
  9.   
  10.     int call()  
  11.     {  
  12.         this->log->LogMsg(msg);  
  13.         return 0;  
  14.     }  
  15.   
  16. private:  
  17.     Logger *log;  
  18.     string msg;  
  19. };  
  20.   
  21. class Logger: public ACE_Task<ACE_MT_SYNCH>  
  22. {  
  23. public:  
  24.     void LogMsg(const string& msg)  
  25.     {  
  26.         cout<<endl<<msg<<endl;  
  27.         ACE_OS::sleep(2);  
  28.     }  
  29.   
  30.     LogMsgCmd *LogMsgActive(const string& msg)  
  31.     {  
  32.         new LogMsgCmd(this,msg);  
  33.     }  
  34. };  
 

這裏對代碼功能做一下簡單的說明:

ACE_Method_Object是ACE提供的命令模式藉口,命令接口調用函數爲int call(),在這裏通過它可以把每個操作日誌的調用封裝爲一個LogMsgCmd對象,這樣,當原來需要調用LogMsg的方法的地方只要調用LogMsgActive即可生成一個LogMsgCmd對象,由於調用LogMsgActive方法,只是對命令進行了封裝,並沒有進行日誌操作,所以該方法會立即返回。然後再新開一個線程,將LogMsgCmd對象作爲參數傳入,在該線程中執行LogMsgCmd對象的call方法,從而實現無阻塞調用。

然而,每次對一個LogMsg調用都開啓一個新線程,無疑是對資源的一種浪費,實際上我們往往將生成的LogMsgCmd對象插入一個命令隊列中,只新開一個命令執行線程依次執行命令隊列中的所有命令。並且,爲了實現對象的封裝,命令隊列和命令執行線程往往也封裝到Logger對象中,代碼如下所示:

Cpp代碼  收藏代碼
  1. #include "ace/OS.h"  
  2. #include "ace/Task.h"  
  3. #include "ace/Method_Object.h"  
  4. #include "ace/Activation_Queue.h"  
  5. #include "ace/Auto_Ptr.h"  
  6.   
  7. #include <string>  
  8. #include <iostream>  
  9. using namespace std;  
  10.   
  11. class Logger: public ACE_Task<ACE_MT_SYNCH>  
  12. {  
  13. public:  
  14.     Logger()  
  15.     {  
  16.         this->activate();  
  17.     }  
  18.   
  19.     int svc();  
  20.     void LogMsg(const string& msg);  
  21.     void LogMsgActive (const string& msg);  
  22.   
  23. private:  
  24.     ACE_Activation_Queue cmdQueue;    //命令隊列  
  25. };  
  26.   
  27. class LogMsgCmd: public ACE_Method_Object  
  28. {  
  29. public:  
  30.     LogMsgCmd(Logger *plog,const string& msg)  
  31.     {  
  32.         this->log=plog;  
  33.         this->msg=msg;  
  34.     }  
  35.   
  36.     int call()  
  37.     {  
  38.         this->log->LogMsg(msg);  
  39.         return 0;  
  40.     }  
  41.   
  42. private:  
  43.     Logger *log;  
  44.     string msg;  
  45. };  
  46.   
  47. void Logger::LogMsg(const string& msg)  
  48. {  
  49.     cout<<endl<<msg<<endl;  
  50.     ACE_OS::sleep(2);  
  51. }  
  52.   
  53. //以主動的方式記錄日誌  
  54. void Logger::LogMsgActive(const string& msg)  
  55. {  
  56.     //生成命令對象,插入到命令隊列中  
  57.     cmdQueue.enqueue(new LogMsgCmd(this,msg));  
  58. }  
  59.   
  60. int Logger::svc()  
  61. {  
  62.     while(true)  
  63.     {  
  64.         //遍歷命令隊列,執行命令  
  65.         auto_ptr<ACE_Method_Object> mo  
  66.             (this->cmdQueue.dequeue ());  
  67.   
  68.         if (mo->call () == -1)  
  69.             break;  
  70.     }  
  71.     return 0;  
  72. }  
  73.   
  74. int main (int argc, ACE_TCHAR *argv[])  
  75. {  
  76.     Logger log;  
  77.     log. LogMsgActive ("hello");  
  78.   
  79.     ACE_OS::sleep(1);  
  80.     log.LogMsgActive("abcd");  
  81.   
  82.     while(true)  
  83.         ACE_OS::sleep(1);  
  84.   
  85.     return 0;  
  86. }  
 

在這裏需要注意一下命令隊列ACE_Activation_Queue對象,它是線程安全的,使用方法比較簡單,這裏我也不多介紹了。

主動對象的基本結構就是這樣,然而,由於主動對象是異步調用的,又引出瞭如下兩個新問題:

  1. 方法調用線程如何知道該方法已經執行完成?
  2. 如何或得方法的返回值?

這兩個問題將在下回給與解決。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章