对于比较耗时的处理(如IO操作),如果采用同步的调用方式,程序则会阻塞在当前调用的函数上,无法进行其它的操作,造成时间浪费,ACE提供了方法对象(ACE_Method_Object),可以实现将同步方法转换为异步方法的机制,下文中将以打印服务为例介绍如何将同步方法转换为异步调用的方法。
此方案采用了多线程处理的方式,将原来的类方法作为一个方法对象(ACE_Method_Object),每一次函数的调用都会创建一个方法对象,之后会将此方法对象加入到活动队列(activation queue),活动对象(ACE_Task)的服务函数中从队列中取出方法对象,并执行函数调用。
下面为日志类,主要包含Say和SayImpl两个函数,一个函数是供用户程序使用,另一个是供方法对象使用。
#ifndef __LOGGER_H__ #define __LOGGER_H__ #include "ace/Task.h" #include "ace/Future.h" #include "ace/Activation_Queue.h" /* *将函数的调用转换为异步调用,实现的原理主要有以下三点 *1.通过创建方法对象 *2.将其压入到活动队列activation_queue *3.出列,并执行实际方法调用 */ class Logger : public ACE_Task<ACE_MT_SYNCH> { public: int open(void*); int close(u_long flags){return 0;} int svc(void); //异步调用函数,负责创建方法对象,并将其压入队列 ACE_Future<u_long>Say(const char* pszMsg); //异步实现函数,实际调用打印函数 u_long SayImpl(const char* pszMsg); private: ACE_Activation_Queue aq; }; #endif
在实现代码中可以看到,每一次的函数调用都会创建一个MO对象,并将其压入队列;SayImpl模拟IO耗时操作。
#include "stdafx.h" #include "Logger.h" #include "ace/Method_Object.h" #include "SayMO.h" int Logger::open(void*) { activate(THR_NEW_LWP); return 0; } //从活动队列中取出方法对象,并执行这个方法 int Logger::svc(void) { while (1) { ACE_DEBUG((LM_DEBUG,ACE_TEXT("%t svc/n"))); auto_ptr<ACE_Method_Object> mo(aq.dequeue()); if (mo->call() == -1) { break; } } return 0; } //异步调用函数,每调用一次Say函数,在内部将会创建对应的方法对象 //将参数和返回值作为方法对象的构造参数使用 //将构造函数的对象压入活动队列 ACE_Future<u_long> Logger::Say(const char* pszMsg) { ACE_Future<u_long> fResult; aq.enqueue(new SayMO(this,pszMsg,fResult)); return fResult; } //异步实现函数 u_long Logger::SayImpl(const char* pszMsg) { ACE_OS::sleep(1); ACE_DEBUG((LM_DEBUG,ACE_TEXT("%t Invoke:%s"),pszMsg)); return 0; }
方法对象是对Say的对象化,实现打印字符串的操作。每一个方法对应一个方法类
#ifndef __SAYMO_H__ #define __SAYMO_H__ #include "ace/Task.h" #include "ace/Future.h" #include "ace/Activation_Queue.h" #include "ace/Method_Object.h" #include "Logger.h" /* *方法对象类 *每一个方法必须对应一个方法类,这个类负责构造方法对象的参数和返回值 */ class SayMO : public ACE_Method_Object { public: SayMO(Logger* pLogger,const char* pszMsg,ACE_Future<u_long>& fResult); ~SayMO(); int call(); private: Logger* m_pLogger; const char* m_pszMsg; ACE_Future<u_long> m_fResult; }; #endif
方法对象类的关键为call方法的实现,通过此方法可以调用Logger对象相关的函数
#include "stdafx.h" #include "SayMO.h" SayMO::SayMO(Logger* pLogger,const char* pszMsg,ACE_Future<u_long>& fResult): m_pLogger(pLogger),m_pszMsg(pszMsg),m_fResult(fResult) { ACE_DEBUG((LM_DEBUG,ACE_TEXT("SayMO Created!/n"))); } SayMO::~SayMO() { ACE_DEBUG((LM_DEBUG,ACE_TEXT("SayMO destroyed!/n"))); } int SayMO::call() { ACE_DEBUG((LM_DEBUG,ACE_TEXT("%t call/n"))); return m_fResult.set(m_pLogger->SayImpl(m_pszMsg)); }
测试程序代码,针对pLogger对象执行100此函数调用,从其输出日志可以看出:每一次的函数调用,SayMO对象会被调用一次,调用完成之后会自动调用销毁函数;主程序线程与调用函数分别位于不同的线程中。
#include "stdafx.h" #include "Logger.h" #include "SayMO.h" int ACE_TMAIN(int argc, ACE_TCHAR* argv[]) { Logger* pLogger = new Logger; ACE_Future<u_long> fResult; pLogger->open(0); for (int i = 0; i < 100;i++) { char pszMsg[50] = {0}; ACE_DEBUG((LM_DEBUG,ACE_TEXT("%t ACE_TMAIN/n"))); ACE_OS::sprintf(pszMsg,ACE_TEXT("d/n"),i); fResult = pLogger->Say(pszMsg); } ACE_Thread_Manager::instance()->wait(); return 0; }