ACE_TASK学习-程序员宅基地

技术标签: 网络  操作系统  

 

参考:

基本上使用了“  ACE_Task介绍(生产者/消费者)v3.0 - 程序员宅基地  http://blog.csdn.net/calmreason/article/details/16922561/ ”中的例子和介绍

ACE_Task框架 与Windows 消息循环对比 - 程序员宅基地  http://blog.csdn.net/zzhongcy/article/details/41379917

ACE的Task框架 - 程序员宅基地  http://blog.csdn.net/dongyu_1989/article/details/72858166

 

一个很好的例子,其中ACE_MESSAGE_Block的用法完全正确,前面两个例子里,有内存泄露之嫌:

用ACE实现的生产者和消费者模式 - 程序员宅基地  http://blog.csdn.net/colder2008/article/details/5838298

 

ACE_Thread_Manager(v3.12) - 程序员宅基地  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实现的生产者和消费者模式 - 程序员宅基地  http://blog.csdn.net/colder2008/article/details/5838298

这是一个很好的例子,其中ACE_MESSAGE_Block的用法完全正确,其他的例子里,有内存泄露之嫌。(详细可百度ACE_MESSAGE_Block的用法)

 

 

ACE_Task面向对象的线程

ACE使用此类来实现主动对象模式。所有希望成为“主动对象”的对象都必须由此类派生。同时可将它看作是更高级的、更为面向对象的线程。

ACE_Task可用作:

<1>更高级的线程(常称其为任务)
<2>主动对象模式中的主动对象
 
  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 - 程序员宅基地  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例子 - 程序员宅基地  http://blog.csdn.net/dongyu_1989/article/details/72868964

ACE_Message_Block例子 - 程序员宅基地  http://blog.csdn.net/dongyu_1989/article/details/72863942

 

ACE_Task的主要方法:
 

    open():初始化资源,如果创建线程,在此方法里面调用 activate

      close():释放资源,svc退出之后会自动调用此方法,常在此释放资源,线程数是多个的时候不可以直接在close里面delete this

      svc():线程的启动位置,线程运行就是执行此函数

     activate():启动线程,可指定线程的数目。它是连接应用程序和框架的纽带,它将ACE_Task实例注册到Task框架中,接受框架的管理。

     putq():放置消息到任务的消息队列中

      getq():从任务的消息队列中取出消息

      thr_count():返回任务中线程的数目

      last_thread():返回任务中将线程计数器从1降为0的线程的ID

 PS: 由于ACE_Task对象一般是在堆中创建的,因此必须要进行释放操作.

class  CTaskDemo : public ACE_Task<ACE_MT_SYNCH>
{
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;
    }
};
 
 

多线程的常用方法

 
等待所有线程退出
下面的这句话通常写在main的最后,表示当所有线程都执行结束的时候程序才退出(否则,某一个线程执行结束程序可能就退出了)。
 

ACE_Thread_Manager::instance()->cancel_all();
ACE_Thread_Manager::instance()->wait();

 

退出当前线程:
下面的这句话写在线程执行的地方
(1)Task的svc方法里面
(2)函数里面,然后用这个函数创建线程。
会让当前线程直接退出。
  1. 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的消息系统,通常要做如下的步骤:

  1. 编写一个派生自ACE_Task的类,指定它的同步模式
    ACE_Task的消息队列可以由多个处理线程共享使用,所以需要提供同步模式,例如 ACE_MT_SYNCH和ACE_NULL_SYNCH分别表示基于多线程的同步和不使用同步,这个参数是ACE_Task的一个模板参数。
  2. class My_Task : public ACE_Task<ACE_MT_SYNCH>
    {
    public:
    	virtual int svc();
    }
    	
  3. 重载 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);
  4. 假设 My_Task是一个基于ACE_Task的类,创建一个唯一的My_Task实例,这个可以通过
    typedef ACE_Singleton<MyTask, SYNCH_METHOD> MYTASK;
    然后总是使用MYTASK::instance方法来获取一个My_Task的指针来完成。
  5. 在适当位置(一般是程序开始的时候),让My_Task开始工作
    MYTASK::intance()->activate(
    THR_NEW_LWP | THR_JOINABLE |THR_INHERIT_SCHED , // 线程创建的属性
    n_threads = 1, // 线程的数目,即有多少处理线程
    ...)
  6. 上面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表示创建一个线程。
  7. 在有消息发生的时候发送消息
    ACE_Message_Block * msg;
    // fill the msg
    ...
    MYTASK::intance()->putq(msg);
  8. 等待所有线程退出,通常写在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:生产者和消费者共享同一个内部消息队列
生产者
ProduceAudio.h
 
  1. #ifndef PRODUCEAUDIO_H  
  2. #define PRODUCEAUDIO_H  
  3.   
  4. #include "ace/Task.h"  
  5.   
  6. class ProduceAudio :  public ACE_Task<ACE_MT_SYNCH>    
  7. {  
  8. public:  
  9.     ProduceAudio(ACE_Thread_Manager *thr_man=0,  
  10.         ACE_Message_Queue<ACE_MT_SYNCH> *mq=0);  
  11.     ~ProduceAudio(void);  
  12.     int open(void*);  
  13.     int svc(void);  
  14. };  
  15.   
  16. #endif  
ProduceAudio.cpp
 
  1. #include "ProduceAudio.h"  
  2.   
  3. #include "ace/Log_Msg.h"  
  4. #include "ace/OS.h"  
  5. #include "Converter.h"  
  6. #include <string>  
  7. using namespace std;  
  8.   
  9. ProduceAudio::ProduceAudio(ACE_Thread_Manager *thr_man,  
  10.     ACE_Message_Queue<ACE_MT_SYNCH> *mq)  
  11.     :ACE_Task<ACE_MT_SYNCH>(thr_man,mq)  
  12. {  
  13. }  
  14.   
  15. ProduceAudio::~ProduceAudio(void)  
  16. {  
  17.     ACE_DEBUG((LM_DEBUG, "(%t) ~ProduceAudio()\n"));    
  18.   
  19. }  
  20.   
  21. int ProduceAudio::open(void*)    
  22. {    
  23.     ACE_DEBUG((LM_DEBUG, "(%t) ProduceAudio task opened\n"));    
  24.     activate(THR_NEW_LWP,1);    
  25.     return 0;    
  26. }   
  27.   
  28. int ProduceAudio::svc(void)  
  29. {  
  30.     ACE_DEBUG((LM_DEBUG, "(%t) ProduceAudio::svc() running\n"));    
  31.     string s("message");  
  32.     for ( int i=0;i<3;++i)  
  33.     {  
  34.         ACE_Message_Block * blk = new ACE_Message_Block(10);  
  35.         blk->copy( (s + lexical_cast<string>(i)).c_str());  
  36.         this->putq(blk);  
  37.         //this->put(blk);  
  38.         ACE_DEBUG((LM_DEBUG, "(%t) ProduceAudio::svc() put(%s),now msg_queue()->message_count()[%d]\n",blk->rd_ptr(),  
  39.             this->msg_queue()->message_count()));    
  40.         ACE_OS::sleep(1);  
  41.     }  
  42.     ACE_DEBUG((LM_DEBUG, "(%t) ProduceAudio::svc() return\n"));    
  43.     return 0;  
  44. }  


消费者
SendToServer.h
  1. #ifndef SENDTOSERVER_H  
  2. #define SENDTOSERVER_H  
  3. #include "ace/Task.h"  
  4.   
  5. class SendToServer :  public ACE_Task<ACE_MT_SYNCH>    
  6. {  
  7. public:  
  8.     SendToServer(ACE_Thread_Manager *thr_man=0,  
  9.         ACE_Message_Queue<ACE_MT_SYNCH> *mq=0);  
  10.     ~SendToServer(void);  
  11.     int open(void*);  
  12.     int svc(void);  
  13. };  
  14.   
  15. #endif  
SendToServer.cpp
 
  1. #include "SendToServer.h"  
  2.   
  3. #include "ace/OS.h"  
  4. #include <string>  
  5. using namespace std;  
  6.   
  7. SendToServer::SendToServer(ACE_Thread_Manager *thr_man,  
  8.     ACE_Message_Queue<ACE_MT_SYNCH> *mq)  
  9.     :ACE_Task<ACE_MT_SYNCH>(thr_man,mq)  
  10. {  
  11. }  
  12.   
  13. SendToServer::~SendToServer(void)  
  14. {  
  15.     ACE_DEBUG((LM_DEBUG, "(%t) ~SendToServer()\n"));    
  16. }  
  17.   
  18. int SendToServer::open(void*)    
  19. {    
  20.     ACE_DEBUG((LM_DEBUG, "(%t) SendToServer task opened\n"));    
  21.     activate(THR_NEW_LWP,1);    
  22.     return 0;    
  23. }   
  24.   
  25. int SendToServer::svc(void)  
  26. {  
  27.     ACE_DEBUG((LM_DEBUG, "(%t) SendToServer::svc() running\n"));    
  28.     ACE_Message_Block * blk = NULL;  
  29.     int count =0;  
  30.     for ( ; count<3;)  
  31.     {  
  32.         if (this->msg_queue()->message_count()>0)  
  33.         {  
  34.             this->getq(blk);  
  35.             ++count;  
  36.             ACE_DEBUG((LM_DEBUG,"SendToServer get :%s\n",blk->rd_ptr()));  
  37.             blk->release();  
  38.         }  
  39.         ACE_OS::sleep(1);  
  40.     }  
  41.     ACE_DEBUG((LM_DEBUG, "(%t) SendToServer::svc() return\n"));    
  42.     return 0;  
  43. }  
主函数main.cpp
  1. #include "ace/Thread_Manager.h"  
  2. #include "SendToServer.h"  
  3. #include "ProduceAudio.h"  
  4.   
  5. #ifdef _DEBUG    
  6. #pragma comment (lib,"ACEd.lib")    
  7. #else    
  8. #pragma comment (lib,"ACE.lib")    
  9. #endif   
  10.   
  11. int main(int argc, char* argv[])  
  12. {  
  13.     SendToServer consumer(NULL,NULL);  
  14.     ProduceAudio producer(NULL,consumer.msg_queue());  
  15.     producer.open(NULL);  
  16.     consumer.open(NULL);  
  17.   
  18.     ACE_Thread_Manager::instance()->wait();  
  19.     return 0;  
  20. }  


生产者消费者实例2:生产者通过引用消费者,来操作消费者的内部消息队列
 
  1. #ifdef _DEBUG  
  2. #pragma comment (lib,"ACEd.lib")  
  3. #else  
  4. #pragma comment (lib,"ACE.lib")  
  5. #endif  
  6.   
  7.   
  8. #include "ace/Log_Msg.h"  
  9. #include "ace/Task.h"  
  10. #include "ace/OS.h"  
  11. #include "ace/Message_Block.h"  
  12. #include <stdio.h>  
  13. #include <string.h>  
  14. #include <iostream>  
  15. #include <string>  
  16. #include <sstream>  
  17. using namespace std;  
  18.   
  19. class My_Data  
  20. {  
  21. public:  
  22.     My_Data(){key = ++id;cout<<"My_Data("<<id<<")\n";}  
  23.     ~My_Data(){cout<<"~My_Data("<<id<<")\n";}  
  24.     string data;  
  25.     int key;  
  26.     static int id;  
  27. };  
  28. int My_Data::id = 0;  
  29.   
  30. class Consumer:  
  31.     public ACE_Task<ACE_MT_SYNCH>  
  32. {  
  33. public:  
  34.     //启动Task消费线程  
  35.     int open(void*)  
  36.     {  
  37.         ACE_DEBUG((LM_DEBUG, "(%t) Consumer task opened\n"));  
  38.         activate(THR_NEW_LWP,1);  
  39.         return 0;  
  40.     }  
  41.     int svc(void)  
  42.     {  
  43.         //Get ready to receive message from Producer  
  44.         do  
  45.         {  
  46.             ACE_Message_Block * msg =0;  
  47.             ACE_DEBUG((LM_DEBUG,"(%t)消费者开始取消息\n"));  
  48.             if (!this->msg_queue()->is_empty())//取消息的时候最好要判断队列是否为空,因为如果刚开始取就是空的,就会阻塞,后来没有人唤醒的话就会一直阻塞  
  49.             {  
  50.                 this->getq(msg);//从消息队列中取出一个消息,这个消息的内存使用权就转接到消息指针上面了。  
  51.                 ACE_DEBUG((LM_DEBUG,"(%t)消费者收到消息: 内容[%s]\n",msg->rd_ptr()));  
  52.                 msg->release();  
  53.             }else  
  54.             {  
  55.                 cout<<"队列空,等待10秒之后再取消息!"<<endl;  
  56.                 ACE_OS::sleep(10);  
  57.             }  
  58.               
  59.         }while(true);  
  60.         return 0;  
  61.     }  
  62.     int close(u_long)  
  63.     {  
  64.         ACE_DEBUG((LM_DEBUG,"Consumer closes down\n"));  
  65.         return 0;  
  66.     }  
  67. };  
  68. class Producer : public ACE_Task<ACE_MT_SYNCH>  
  69. {  
  70. public:  
  71.     Producer(Consumer * consumer):consumer_(consumer){}  
  72.     int open(void*)  
  73.     {  
  74.         ACE_DEBUG((LM_DEBUG, "(%t) Producer task opened\n"));  
  75.         activate(THR_NEW_LWP,1);  
  76.         return 0;  
  77.     }  
  78.     //The Service Processing routine  
  79.     int svc(void)  
  80.     {  
  81.         //生产者深入一个用户名,放到消费者的队列中  
  82.         do   
  83.         {  
  84.             My_Data one_data;  
  85.             ACE_OS::sleep(1);//防止CPU使用率过高  
  86.             ostringstream os;  
  87.             os<<one_data.key;  
  88.             one_data.data = "name" + os.str();  
  89.   
  90.             ACE_Message_Block* mb = new ACE_Message_Block(100);  
  91.             mb->copy(one_data.data.c_str());  
  92.             cout<<"将"<<mb->rd_ptr()<<"放入到了队列中\n";  
  93.             this->consumer_->putq(mb);  
  94.         } while (shutdown);  
  95.         return 0;  
  96.     }  
  97.     int close(u_long)  
  98.     {  
  99.         ACE_DEBUG((LM_DEBUG,"Producer closes down\n"));  
  100.         return 0;  
  101.     }  
  102. private:  
  103.     Consumer * consumer_;  
  104. };  
  105. int main(int argc, char * argv[])  
  106. {  
  107.     Consumer * consumer = new Consumer;  
  108.     Producer * producer = new Producer(consumer);  
  109.     producer->open(0);  
  110.     consumer->open(0);  
  111.     //Wait for all the tasks to exit.  
  112.     ACE_Thread_Manager::instance()->wait();  
  113.     ACE_OS::system("pause");  
  114.     delete producer;  
  115.     delete consumer;  
  116.     return 0;  
  117. }  


 
分析:
以上为经典的生产者-消费者例子,演示了两个任务如何使用底层的消息队列进行通信。我们可以将生产者和消费者看作是不同的ACE_Task类型的对象。方案十分简单,但却是面向对象的,在编写面向对象的多线程程序或主动对象的实例时,我们可采用此方案,它提供了比低级线程API更好的方法。
 

多个Task共用一个消息队列

多个Task共用一个消息队列,可以使用ACE_Task的接口来方便的实现
 
  1.    SendToServer consumer(NULL,NULL);  
  2. ProduceAudio producer(NULL,consumer.msg_queue());  
  3. producer.msg_queue()->high_water_mark((size_t)(1024*1024*2));  
  4. consumer.open(NULL);  
  5. producer.open(NULL);  
上面的consumer、producer都是ACE_Task的派生类对象,注意是生产者将自己的消息队列指定为消费者的,这样消费者消费自己的数据的时候其实就是生产者生产的了。
 
 

一个Task中开启多个线程

例子1: 
[ACE程序员教程笔记]ACE_Connector使用一个连接多个线程发送数据 - 程序员宅基地  http://blog.csdn.net/maxcode/article/details/6126551
 
例子2: 
下面的例子是结合Proactor模式来使用的,举这个例子只是为了演示一个Task如何开启多个线程,所以只关心线程部分。
一个例子:
头文件

#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;
}

 

转载于:https://www.cnblogs.com/nanzhi/p/8482533.html

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_30583563/article/details/97672817

智能推荐

【2024计算机毕业设计】基于java+jsp+Spring+hibernate+Struts2的SSH通讯录管理系统-程序员宅基地

文章浏览阅读551次,点赞9次,收藏16次。点此进入演示地址最好是java jdk 1.8,我在这个平台上运行的。其他版本理论上也可以。Eclipse,Myeclipse,IDEA或者Spring Tool Suite都可以,如果编译器的版本太低,需要升级下编译器,不要弄太低的版本Windows XP/7/8//8.1/10/11或者Linux或者MacOS,2G内存以上,推荐4G,4G以上更好管理员角色包含以下功能:管理员登录页面,分组信息管理,分组信息编辑,编辑联系人信息,联系人增删改查,密码修改等功能。

adb 安装与使用(一)-程序员宅基地

文章浏览阅读3.3k次,点赞2次,收藏10次。一、ADB简介1. 什么是adb?  adb(Android Debug Bridage)是Android sdk的一个工具;  adb 是用来连接安卓手机和PC端的桥梁,要有adb作为二者之间的维系,才能让用户在电脑上对手机进行全面的操作。  Android 的初衷是用adb这样的一个工具来协助开发人员在开发和拷贝Android 应用的过程中更好的调试apk ,因此ad..._如何安装adb工具包

Neo4j入门_neo4j入门教程-程序员宅基地

文章浏览阅读440次。文章目录1. 安装2. 导入CSV数据1. LOAD CSV3. 创建图结构1. 安装从neo4j官网下载需要的desktop社区版本(免费),windows/osx/linux,下载之前需要注册登记个人信息,之后会给出激活码,用于后续安装过程中软件的激活。具体的安装步骤,官网也给出了,个人用的osx版本。下载完成之后,选择激活码的方式进行软件环境加载,如下图。加载完成之后的页面。2. 导入CSV数据以下内容翻译自官方教程根据数据量的大小,有三种方式导入方式,不同方式的标准和功能也不_neo4j入门教程

[Linux] Windows安装Linux子系统教程-程序员宅基地

文章浏览阅读4.3k次,点赞3次,收藏14次。我们想在windows系统中想用linux系统,用的最多的就是虚拟机了吧,今天介绍一种不用装虚拟机在windows也能使用linux系统的一个方法,该方法全程使用命令,但对于常用linux的人来说也不是什么难事,最主要是精简,不用打开虚拟机,非常方便,特此记录一下。_windows安装linux子系统

学习ESP8266②AT指令开发获取网络时间_esp8266wifi模块获取网络时间-程序员宅基地

文章浏览阅读9.3k次,点赞8次,收藏38次。用到的指令介绍 AT+CWMODE 配置wifi模式 AT+CWJAP 连接AP AT+CIPSTART 建立TCP连接 AT+CIPSEND 透传发送数据API接口:http://cgi.im.qq.com/cgi-bin/cgi_svrtime具体过程 先配置WiFi,esp8266连上路由器AT+CWMODE=3AT+CWJAP=&amp;amp;amp;quot;HONOR9&amp;amp;amp;quot;,&amp;amp;amp;quot;12_esp8266wifi模块获取网络时间

C语言程序设计学习笔记第九章结构体_c语言谭浩强第五版宏定义在哪章-程序员宅基地

文章浏览阅读871次。第九章结构体9.1结构体的概念结构体是一种可以由我们自己定义的数据类型#include <stdio.h>struct student{ int num; int score; float average;};int main(void){ //定义变量,存放学生的序号、成绩、平均分 struct student Tom;//Tom是变量,struct student是数据类型 struct student cla_c语言谭浩强第五版宏定义在哪章

随便推点

OSDI, SOSP与美国著名计算机系的调查(2008)_sosp发文统计-程序员宅基地

文章浏览阅读932次。【序言】按照USnews的分类,Computer Science被分为四个大类:AI, Programming Language,Systems,Theory.。毫无疑问,Systems是这四个大类中最要紧也是最大的一个。根据citeseer在2003年5月排出的所有计算机学科会议和期刊的影响因子排名[1],在前十位中的属于Systems的有7个,在前15个中占据了11_sosp发文统计

android studio 模拟器 简书,Android Studio | 配置模拟器AVD存放路-程序员宅基地

文章浏览阅读238次。Android Studio 配置模拟器AVD存放路Android Studio安装之后,默认的会给我们创建一个Nexus的模拟器,这个模拟器的镜像文件放在了C:\Users\Administrator\.android中其中的avd文件夹就是用来存放,模拟器镜像文件的。相当占用c盘空间,像我这种c盘空间不大的,看着真的不爽。好了现在点进去C:\Users\Administrator\..._centos7 androidstudio emulator avd 配置

CWE/SANS Top 25 Most Dangerous Programming Error-程序员宅基地

文章浏览阅读723次。我只是专贴一下,出处可以参考http://cwe.mitre.org/top25/#Brief希望大家在工作过程中都能够注意这些细节,质量体现于这些细节,打造高质量的软件产品,这些可是基石哦,呵呵 The 2009 CWE/SANS Top 25 Most Dangerous Programming Errors is a list of the most significant progr..._sans top 25

hive分区分桶操作及加载数据_hive 分桶表正确加载数据的方法-程序员宅基地

文章浏览阅读5.1k次,点赞2次,收藏4次。转载来自:http://www.codeweblog.com/hive-%E5%9F%BA%E7%A1%80-1-%E5%88%86%E5%8C%BA-%E6%A1%B6-sort-merge-bucket-join/Hive 已是目前业界最为通用、廉价的构建大数据时代数据仓库的解决方案了,虽然也有 Impala 等后起之秀,但目前从功能、稳定性等方面来说,Hive 的地位尚不_hive 分桶表正确加载数据的方法

freeRtOS的基本概念和使用方法,以及入门方法_feerrtos-程序员宅基地

文章浏览阅读221次,点赞9次,收藏4次。FreeRTOS是一款免费开源、基于实时的操作系统内核。它专门设计用于嵌入式系统,可以在各种处理器架构上运行,并提供了一套稳定的多任务处理机制。_feerrtos

byte[]转int-程序员宅基地

文章浏览阅读207次。第一种: /** * 将int数值转换为占四个字节的byte数组,本方法适用于(低位在前,高位在后)的顺序。 和bytesToInt()配套使用 * @param value * 要转换的int值 * @return byte数组 */ pub..._byte转int

推荐文章

热门文章

相关标签