Task框架与其他框架不同,它没有对应的框架模式,可以将Task框架看成是ACE的多线程编程接口。Task框架分为3部分来分析:第一部分是ACE用于线程间通信的消息队列;第二部分是ACE对操作系统多线程接口的面相对象的封装,在linux环境下主要是对POSIX API的封装;第三部分是ACE的多线程编程接口,它是第一,二部分更高层的封装。
Task框架通过在对象的上下文创建线程,在独立的线程间传递消息,来提高网络并发程序的模块性和可扩展性。Task框架类结构如下图:
ACE_Message_Queue类则是ACE实现的一种消息队列,用于线程间通信,当然也可以应用于其他场景,每个ACE_Task类都有一个消息队列,将消息队列和线程集成在一起,可以大大简化线程间通信编程。
ACE_Thread_Manager类用于线程管理,它是ACE对各种平台下线程编程接口的封装。它是一个线程管理仓库,用来创建和销毁线程,因此我们把它称为线程管理器。每一个通过Task框架创建的线程都有一个线程描述符对象保存在线程管理器的仓库中,用于管理线程的运行状态。
Task框架应用实例
#ifndef CONSUMER_H_
#define CONSUMER_H_
#include "ace/Task.h"
#include "ace/Message_Block.h"
//The Consumer Task.
class Consumer:
public ACE_Task<ACE_MT_SYNCH>
{
public:
int open(void*)
{
ACE_DEBUG((LM_DEBUG, "(%t) Producer task opened \n"));
//Activate the Task
activate(THR_NEW_LWP,1);
return 0;
}
//The Service Processing routine
int svc(void)
{
//Get ready to receive message from Producer
ACE_Message_Block * mb =0;
do
{
mb=0;
//Get message from underlying queue
getq(mb);
ACE_DEBUG((LM_DEBUG,
"(%t)Got message: %d from remote task\n",*mb->rd_ptr()));
}while(*mb->rd_ptr()<10);
return 0;
}
int close(u_long)
{
ACE_DEBUG((LM_DEBUG,"Consumer closes down \n"));
return 0;
}
};
#endif /* CONSUMER_H_ */
#ifndef PRODUCTER_H_
#define PRODUCTER_H_
#include "ace/Task.h"
#include "ace/Message_Block.h"
#include "Consumer.h"
class Producer:
public ACE_Task<ACE_MT_SYNCH>
{
public:
Producer(Consumer * consumer):
consumer_(consumer), data_(0)
{
mb_=new ACE_Message_Block((char*)&data_,sizeof(data_));
}
int open(void*)
{
ACE_DEBUG((LM_DEBUG, "(%t) Producer task opened \n"));
//Activate the Task
activate(THR_NEW_LWP,1);
return 0;
}
//The Service Processing routine
int svc(void)
{
while(data_<11)
{
//Send message to consumer
ACE_DEBUG((LM_DEBUG,
"(%t)Sending message: %d to remote task\n",data_));
consumer_->putq(mb_);
//Go to sleep for a sec.
ACE_OS::sleep(1);
data_++;
}
return 0;
}
int close(u_long)
{
ACE_DEBUG((LM_DEBUG,"Producer closes down \n"));
return 0;
}
private:
char data_;
Consumer * consumer_;
ACE_Message_Block * mb_;
};
#endif /* PRODUCTER_H_ */
#include "Consumer.h"
#include "Producter.h"
int main(int argc, char * argv[])
{
Consumer *consumer = new Consumer;
Producer *producer = new Producer(consumer);
producer->open(0);
consumer->open(0);
//Wait for all the tasks to exit.
ACE_Thread_Manager::instance()->wait();
}
从示例中我们可以看出,在并发编程中使用Task框架有以下几个优势:
- 应用程序开发人员无须从零开始开发,Task框架已经提供了消息队列和多线程编程接口。
- Task框架的消息队列和多线程接口功能丰富,能够满足应用程序的绝大部分要求。
- 相比原始的系统接口,Task框架的多线程编程接口使用更加方便,而且还提供了跨平台的功能。
从上面生产者消费者的例子中,可以推断出以下结论:
- ACE_Task类是Task框架提供的应用程序接口,而svc函数则是接口的模板方法,应用程序必须实现它。
- activate函数是连接应用程序和框架的纽带,它将ACE_Task实例注册到Task框架中,接受框架的管理。
- 有一个仓库用于插入,查找和删除ACE_Task实例。
- 有一个控制器用于管理整个框架,它一直隐藏在幕后。在示例代码中,ACE_Thread_Manager就是这样一个线程控制器。
ACE消息队列实现分析
ACE并没有使用普通的队列机制,比如STL的队列容器,而是设计了一个功能强大的消息队列——ACE_Message_Queue。ACE_Message_Queue类提供了队列的数据和操作接口,ACE_Message_Block类是消息队列中的数据单元,ACE_Data_Block类用于封装应用程序的数据。ACE_Date_Block类封装的应用程序的数据既可以是数据,又可以是指针。同时提供这两种封装方式可以提高框架的灵活性,而提高对指针的封装,可以避免数据复制,提高框架的性能。应用程序只需要关注ACE_Message_Block对象。ACE_Data_Block对象默认情况隐藏在ACE_Message_Block对象内部,应用程序不需要关注,这样可以简化应用程序对队列元素的操作。