ACE_TASK学习
参考:
基本上使用了“ ACE_Task介绍(生产者/消费者)v3.0 - CSDN博客 http://blog.csdn.net/calmreason/article/details/16922561/ ”中的例子和介绍
ACE_Task框架 与Windows 消息循环对比 - CSDN博客 http://blog.csdn.net/zzhongcy/article/details/41379917
ACE的Task框架 - CSDN博客 http://blog.csdn.net/dongyu_1989/article/details/72858166
一个很好的例子,其中ACE_MESSAGE_Block的用法完全正确,前面两个例子里,有内存泄露之嫌:
用ACE实现的生产者和消费者模式 - CSDN博客 http://blog.csdn.net/colder2008/article/details/5838298
ACE_Thread_Manager(v3.12) - CSDN博客 http://blog.csdn.net/calmreason/article/details/36399697
ACE_Task框架结构:
Task框架与其他框架不同,它没有对应的框架模式,可以将Task框架看成是ACE的多线程编程接口。Task框架分为3部分来分析:第一部分是ACE用于线程间通信的消息队列;第二部分是ACE对操作系统多线程接口的面相对象的封装,在linux环境下主要是对POSIX API的封装;第三部分是ACE的多线程编程接口,它是第一,二部分更高层的封装。
Task框架通过在对象的上下文创建线程,在独立的线程间传递消息,来提高网络并发程序的模块性和可扩展性。Task框架类结构如下图:
ACE_Message_Queue类则是ACE实现的一种消息队列,用于线程间通信(TaskA将消息putq进入TaskB的消息队列,TaskB从自己的消息队列中getq获得这个消息,从而实现通信),
当然也可以应用于其他场景,每个ACE_Task类都有一个消息队列,将消息队列和线程集成在一起,可以大大简化线程间通信编程。
ACE_Thread_Manager类用于线程管理,它是ACE对各种平台下线程编程接口的封装。它是一个线程管理仓库,用来创建和销毁线程,因此我们把它称为线程管理器。
每一个通过Task框架创建的线程都有一个线程描述符对象保存在线程管理器的仓库中,用于管理线程的运行状态。
一个经典的 Task框架应用实例就是生产者,消费者模式,例子:
用ACE实现的生产者和消费者模式 - CSDN博客 http://blog.csdn.net/colder2008/article/details/5838298
这是一个很好的例子,其中ACE_MESSAGE_Block的用法完全正确,其他的例子里,有内存泄露之嫌。(详细可百度ACE_MESSAGE_Block的用法)
ACE_Task面向对象的线程
ACE使用此类来实现主动对象模式。所有希望成为“主动对象”的对象都必须由此类派生。同时可将它看作是更高级的、更为面向对象的线程。
ACE_Task可用作:
ACE消息队列实现分析
ACE_Task封装了任务,每个任务都含有一或多个线程,以及一个底层消息队列。各个任务通过这些消息队列进行通信。
发送任务用putq() 将消息插入到另一任务的消息队列中,接收任务通过使用getq()将消息提取出来。
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对象内部,
应用程序不需要关注,这样可以简化应用程序对队列元素的操作。
ACE_Message_Block和ACE_Message_Queue
ACE Message_Queue由一或多个通过prev_和next_指针链接在一起的Message_Block组成。这样的结构可以高效地操作任意大的消息,而不会导致巨大的内存拷贝开销。
ace 之 ACE_Message_Block - CSDN博客 http://blog.csdn.net/wskdgv666/article/details/49536053
ACE_Message_Block消息数据类 - 炽离 - 博客园 http://www.cnblogs.com/hgwang/p/5940168.html
ACE: ACE_Message_Queue<> Class Template Reference http://www.dre.vanderbilt.edu/Doxygen/5.4.8/html/ace/classACE__Message__Queue.html
ACE_Message_Queue例子 - CSDN博客 http://blog.csdn.net/dongyu_1989/article/details/72868964
ACE_Message_Block例子 - CSDN博客 http://blog.csdn.net/dongyu_1989/article/details/72863942
ACE_Task的主要方法:
open():初始化资源,如果创建线程,在此方法里面调用 activate
close():释放资源,svc退出之后会自动调用此方法,常在此释放资源,线程数是多个的时候不可以直接在close里面delete this
svc():线程的启动位置,线程运行就是执行此函数
putq():放置消息到任务的消息队列中
getq():从任务的消息队列中取出消息
thr_count():返回任务中线程的数目
last_thread():返回任务中将线程计数器从1降为0的线程的ID
PS: 由于ACE_Task对象一般是在堆中创建的,因此必须要进行释放操作.
{
public:
virtual int open (void *args = 0)
{
activate( THR_NEW_LWP, 1 );
return 0;
}
virtual int close (u_long flags = 0)
{
if ( ACE_OS::thr_equal ( ACE_Thread::self (), this->last_thread () ) )
{
//释放对象
delete this;
}
return 0;
}
virtual int svc (void)
{
return 0;
}
};
多线程的常用方法
等待所有线程退出
ACE_Thread_Manager::instance()->cancel_all();
ACE_Thread_Manager::instance()->wait();
退出当前线程:
下面的这句话写在线程执行的地方- ACE_Thread_Manager::instance()->exit();
与ACE_Task_Base的关系
ACE_Task_Base是主动对象的基类,ACE_Task继承了ACE_Task_Base的线程功能之后添加了具有同步策略功能的消息队列ACE_Message_Queue。
如果你只需要一个线程对象,你可以直接使用ACE_Task_Base
同步模式
分两种: ACE_MT_SYNCH(多线程)和ACE_NULL_SYNCH(单线程)。
多线程模式下线程的消息队列会使用多线程同步策略,会造成线程的阻塞;单线程模式下不存在同步的额外开销;多线程下保证一个线程对象在同一时刻只有一个方法在执行。
ACE_Task可以启动一个或多个线程,以及一个底层消息队列。各个任务通过消息队列进行通信。至于消息队列实现的内在细节程序员不必关注。
Message_Queue类包含在Task类中。Message_Queue可被同步策略类型参数化,以获取所期望的并发控制级。缺省地,并发控制级是“线程安全”。
如果MT_Synch被用于实例化Message_Queue,所有的公共方法都将是线程安全的,但同时也带来相应的开销。相反,
如果Null_Synch类用于实例化Message_Queue,所有公共方法都不是线程安全的,同时也就没有额外的开销。
搭建ACE_TASK
要搭架一个基于ACE_Task的消息系统,通常要做如下的步骤:
- 编写一个派生自ACE_Task的类,指定它的同步模式
ACE_Task的消息队列可以由多个处理线程共享使用,所以需要提供同步模式,例如 ACE_MT_SYNCH和ACE_NULL_SYNCH分别表示基于多线程的同步和不使用同步,这个参数是ACE_Task的一个模板参数。 -
class My_Task : public ACE_Task<ACE_MT_SYNCH> { public: virtual int svc(); }
- 重载 ACE_Task的 svc 方法,编写消息循环相关的代码,操作消息队列。当然也可以不使用消息队列,将svc作为一般线程函数使用也是可以的,例如使用recv方法接收udp、tcp数据等
int My_Task::svc() { ACE_Message_Block * msg; while(getq(msg) != -1) // int putq (ACE_Message_Block *, ACE_Time_Value *timeout = 0); { // process msg here } }
svc 方法相当与处理线程的入口方法。ACE_Task<ACE_MT_SYNCH> 类自带一个消息队列取消息的方法是:this->getq(blk);放消息的方法是:this->putq(blk);//注意不是this->put(blk); - 假设 My_Task是一个基于ACE_Task的类,创建一个唯一的My_Task实例,这个可以通过
typedef ACE_Singleton<MyTask, SYNCH_METHOD> MYTASK;
然后总是使用MYTASK::instance方法来获取一个My_Task的指针来完成。 - 在适当位置(一般是程序开始的时候),让My_Task开始工作
MYTASK::intance()->activate(
THR_NEW_LWP | THR_JOINABLE |THR_INHERIT_SCHED , // 线程创建的属性
n_threads = 1, // 线程的数目,即有多少处理线程
...) -
上面5中的activaate方法一般在open中调用,调用完activate方法之后svc方法自动运行。而open方法可以像5中描述的那样,在适当的位置调用
指定线程的创建标志:在activate方法执行的时候可以指定线程的内部类型,THR_DETACHED(分离的,可以直接被ACE_Thread_Manager::wait()方法来回收),
默认情况下线程的内部类型是THR_JOINABLE(可结合的,此线程退出的状态会被其他线程捕获并作出相应的处理);
THR_NEW_LWP (挂钩到内核级线程,会创建一个内核线程);
你的线程如果在独立运行一般你会使用:activate(THR_NEW_LWP | THR_BOUND | THR_DETACHED,1);来创建你的线程,1表示创建一个线程。 - 在有消息发生的时候发送消息
ACE_Message_Block * msg; // fill the msg ... MYTASK::intance()->putq(msg);
-
等待所有线程退出,通常写在main函数的最后:ACE_Thread_Manager::instance()->wait();
最后考虑一个使用ACE_Task的实例,在一个编写WEB服务器的项目中,类 Request_Handler负责处理HTTP请求,Request_Hanlder派生自ACE_Task,当有请求时,其他的代码将Http请求构造成一个ACE_Message_Block,并调用Request_Handler的putq方法将请求插入消息队列,Request_Handler配置为根据CPU的数目创建处理线程,Request_Handler的svc方法从队列中获取请求进行处理,然后将处理的结果构造成为一个ACE_Message_Block,插入到Response_Handler的消息队列,Response_Handler也派生自ACE_Task,但它只有一个处理线程,它仅仅将相应的数据写回给客户端。
生产者消费者实例1:生产者和消费者共享同一个内部消息队列
- #ifndef PRODUCEAUDIO_H
- #define PRODUCEAUDIO_H
- #include "ace/Task.h"
- class ProduceAudio : public ACE_Task<ACE_MT_SYNCH>
- {
- public:
- ProduceAudio(ACE_Thread_Manager *thr_man=0,
- ACE_Message_Queue<ACE_MT_SYNCH> *mq=0);
- ~ProduceAudio(void);
- int open(void*);
- int svc(void);
- };
- #endif
- #include "ProduceAudio.h"
- #include "ace/Log_Msg.h"
- #include "ace/OS.h"
- #include "Converter.h"
- #include <string>
- using namespace std;
- ProduceAudio::ProduceAudio(ACE_Thread_Manager *thr_man,
- ACE_Message_Queue<ACE_MT_SYNCH> *mq)
- :ACE_Task<ACE_MT_SYNCH>(thr_man,mq)
- {
- }
- ProduceAudio::~ProduceAudio(void)
- {
- ACE_DEBUG((LM_DEBUG, "(%t) ~ProduceAudio()\n"));
- }
- int ProduceAudio::open(void*)
- {
- ACE_DEBUG((LM_DEBUG, "(%t) ProduceAudio task opened\n"));
- activate(THR_NEW_LWP,1);
- return 0;
- }
- int ProduceAudio::svc(void)
- {
- ACE_DEBUG((LM_DEBUG, "(%t) ProduceAudio::svc() running\n"));
- string s("message");
- for ( int i=0;i<3;++i)
- {
- ACE_Message_Block * blk = new ACE_Message_Block(10);
- blk->copy( (s + lexical_cast<string>(i)).c_str());
- this->putq(blk);
- //this->put(blk);
- ACE_DEBUG((LM_DEBUG, "(%t) ProduceAudio::svc() put(%s),now msg_queue()->message_count()[%d]\n",blk->rd_ptr(),
- this->msg_queue()->message_count()));
- ACE_OS::sleep(1);
- }
- ACE_DEBUG((LM_DEBUG, "(%t) ProduceAudio::svc() return\n"));
- return 0;
- }
- #ifndef SENDTOSERVER_H
- #define SENDTOSERVER_H
- #include "ace/Task.h"
- class SendToServer : public ACE_Task<ACE_MT_SYNCH>
- {
- public:
- SendToServer(ACE_Thread_Manager *thr_man=0,
- ACE_Message_Queue<ACE_MT_SYNCH> *mq=0);
- ~SendToServer(void);
- int open(void*);
- int svc(void);
- };
- #endif
- #include "SendToServer.h"
- #include "ace/OS.h"
- #include <string>
- using namespace std;
- SendToServer::SendToServer(ACE_Thread_Manager *thr_man,
- ACE_Message_Queue<ACE_MT_SYNCH> *mq)
- :ACE_Task<ACE_MT_SYNCH>(thr_man,mq)
- {
- }
- SendToServer::~SendToServer(void)
- {
- ACE_DEBUG((LM_DEBUG, "(%t) ~SendToServer()\n"));
- }
- int SendToServer::open(void*)
- {
- ACE_DEBUG((LM_DEBUG, "(%t) SendToServer task opened\n"));
- activate(THR_NEW_LWP,1);
- return 0;
- }
- int SendToServer::svc(void)
- {
- ACE_DEBUG((LM_DEBUG, "(%t) SendToServer::svc() running\n"));
- ACE_Message_Block * blk = NULL;
- int count =0;
- for ( ; count<3;)
- {
- if (this->msg_queue()->message_count()>0)
- {
- this->getq(blk);
- ++count;
- ACE_DEBUG((LM_DEBUG,"SendToServer get :%s\n",blk->rd_ptr()));
- blk->release();
- }
- ACE_OS::sleep(1);
- }
- ACE_DEBUG((LM_DEBUG, "(%t) SendToServer::svc() return\n"));
- return 0;
- }
- #include "ace/Thread_Manager.h"
- #include "SendToServer.h"
- #include "ProduceAudio.h"
- #ifdef _DEBUG
- #pragma comment (lib,"ACEd.lib")
- #else
- #pragma comment (lib,"ACE.lib")
- #endif
- int main(int argc, char* argv[])
- {
- SendToServer consumer(NULL,NULL);
- ProduceAudio producer(NULL,consumer.msg_queue());
- producer.open(NULL);
- consumer.open(NULL);
- ACE_Thread_Manager::instance()->wait();
- return 0;
- }
生产者消费者实例2:生产者通过引用消费者,来操作消费者的内部消息队列
- #ifdef _DEBUG
- #pragma comment (lib,"ACEd.lib")
- #else
- #pragma comment (lib,"ACE.lib")
- #endif
- #include "ace/Log_Msg.h"
- #include "ace/Task.h"
- #include "ace/OS.h"
- #include "ace/Message_Block.h"
- #include <stdio.h>
- #include <string.h>
- #include <iostream>
- #include <string>
- #include <sstream>
- using namespace std;
- class My_Data
- {
- public:
- My_Data(){key = ++id;cout<<"My_Data("<<id<<")\n";}
- ~My_Data(){cout<<"~My_Data("<<id<<")\n";}
- string data;
- int key;
- static int id;
- };
- int My_Data::id = 0;
- class Consumer:
- public ACE_Task<ACE_MT_SYNCH>
- {
- public:
- //启动Task消费线程
- int open(void*)
- {
- ACE_DEBUG((LM_DEBUG, "(%t) Consumer task opened\n"));
- activate(THR_NEW_LWP,1);
- return 0;
- }
- int svc(void)
- {
- //Get ready to receive message from Producer
- do
- {
- ACE_Message_Block * msg =0;
- ACE_DEBUG((LM_DEBUG,"(%t)消费者开始取消息\n"));
- if (!this->msg_queue()->is_empty())//取消息的时候最好要判断队列是否为空,因为如果刚开始取就是空的,就会阻塞,后来没有人唤醒的话就会一直阻塞
- {
- this->getq(msg);//从消息队列中取出一个消息,这个消息的内存使用权就转接到消息指针上面了。
- ACE_DEBUG((LM_DEBUG,"(%t)消费者收到消息: 内容[%s]\n",msg->rd_ptr()));
- msg->release();
- }else
- {
- cout<<"队列空,等待10秒之后再取消息!"<<endl;
- ACE_OS::sleep(10);
- }
- }while(true);
- return 0;
- }
- int close(u_long)
- {
- ACE_DEBUG((LM_DEBUG,"Consumer closes down\n"));
- return 0;
- }
- };
- class Producer : public ACE_Task<ACE_MT_SYNCH>
- {
- public:
- Producer(Consumer * consumer):consumer_(consumer){}
- int open(void*)
- {
- ACE_DEBUG((LM_DEBUG, "(%t) Producer task opened\n"));
- activate(THR_NEW_LWP,1);
- return 0;
- }
- //The Service Processing routine
- int svc(void)
- {
- //生产者深入一个用户名,放到消费者的队列中
- do
- {
- My_Data one_data;
- ACE_OS::sleep(1);//防止CPU使用率过高
- ostringstream os;
- os<<one_data.key;
- one_data.data = "name" + os.str();
- ACE_Message_Block* mb = new ACE_Message_Block(100);
- mb->copy(one_data.data.c_str());
- cout<<"将"<<mb->rd_ptr()<<"放入到了队列中\n";
- this->consumer_->putq(mb);
- } while (shutdown);
- return 0;
- }
- int close(u_long)
- {
- ACE_DEBUG((LM_DEBUG,"Producer closes down\n"));
- return 0;
- }
- private:
- Consumer * consumer_;
- };
- 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();
- ACE_OS::system("pause");
- delete producer;
- delete consumer;
- return 0;
- }
多个Task共用一个消息队列
- SendToServer consumer(NULL,NULL);
- ProduceAudio producer(NULL,consumer.msg_queue());
- producer.msg_queue()->high_water_mark((size_t)(1024*1024*2));
- consumer.open(NULL);
- producer.open(NULL);
一个Task中开启多个线程
例子1:
例子2:
#include "ace/proactor.h"
#include "ace/task_t.h"
#include "ace/thread_semaphore.h"
#include "ace/WIN32_Proactor.h"
class CProactorTask:public ACE_Task<ACE_MT_SYNCH>
{
public:
CProactorTask(void);
virtual ~CProactorTask(void);
int Start(const int nMax);
int Stop(void);
int Create(void);
int Release(void);
virtual int svc(void);
protected:
ACE_Thread_Semaphore m_sem; //信号量
ACE_Proactor *m_pProactor; // 完成端口对象指针
};
#include "CProactorTask.h"
CProactorTask::CProactorTask(void)
{
}
CProactorTask::~CProactorTask(void)
{
}
//创建完成端口对象
int CProactorTask::Create(void)
{
ACE_WIN32_Proactor *proactor_impl=0;
//新建
ACE_NEW_RETURN(proactor_impl, ACE_WIN32_Proactor, -1);
//关联
ACE_NEW_RETURN(this->m_pProactor, ACE_Proactor(proactor_impl, 1 ), -1);
//保存
ACE_Proactor::instance(this->m_pProactor, 1);
return 0;
}
//启动线程池
int CProactorTask::Start(const int nMax)
{
//创建完成端口对象
Create();
//创建线程
this->activate(THR_NEW_LWP,nMax);
int i;
//保证所有线程已启动
for(i=nMax;i>0;i--)
{
m_sem.acquire();
//Block the thread until the semaphore count becomes greater than 0, then decrement it.
}
printf("start\n");
return 0;
}
//删除线程池
int CProactorTask::Stop(void)
{
ACE_Proactor::event_loop_done();
this->wait();
return 0;
}
//
//每个线程调用
//
int CProactorTask::svc(void)
{
ACE_DEBUG((LM_INFO,ACE_TEXT("create a new ACE_Proactor::run_event_loop!\n")));
//Increment the semaphore by 1
m_sem.release(1);
ACE_Proactor::run_event_loop();
return 0;
}
//
//释放
//
int CProactorTask::Release(void)
{
ACE_Proactor::close_singleton();
m_pProactor = 0;
printf("release\n");
return 0;
}
int ACE_TMAIN(int ,char*[])
{
printf("*************Echo Server************\n");
//获取CPU数量
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
int threadNum=sysInfo.dwNumberOfProcessors<<1; //cpu*2
//开启线程
CProactorTask task;
task.Start(threadNum);
ACE_INET_Addr serverAddr(5151);
ACE_Asynch_Acceptor<TCPTransfer> acceptor;
acceptor.open(serverAddr);
UDPTransfer UDPR;
UDPR.open_addr(serverAddr);
ACE_DEBUG ((LM_DEBUG,
"(%P | %t):Test ends\n"));
ACE_Thread_Manager::instance ()->wait ();
return 0;
}