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

智能推荐

c# 调用c++ lib静态库_c#调用lib-程序员宅基地

文章浏览阅读2w次,点赞7次,收藏51次。四个步骤1.创建C++ Win32项目动态库dll 2.在Win32项目动态库中添加 外部依赖项 lib头文件和lib库3.导出C接口4.c#调用c++动态库开始你的表演...①创建一个空白的解决方案,在解决方案中添加 Visual C++ , Win32 项目空白解决方案的创建:添加Visual C++ , Win32 项目这......_c#调用lib

deepin/ubuntu安装苹方字体-程序员宅基地

文章浏览阅读4.6k次。苹方字体是苹果系统上的黑体,挺好看的。注重颜值的网站都会使用,例如知乎:font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, PingFang SC, Microsoft YaHei, Source Han Sans SC, Noto Sans CJK SC, W..._ubuntu pingfang

html表单常见操作汇总_html表单的处理程序有那些-程序员宅基地

文章浏览阅读159次。表单表单概述表单标签表单域按钮控件demo表单标签表单标签基本语法结构<form action="处理数据程序的url地址“ method=”get|post“ name="表单名称”></form><!--action,当提交表单时,向何处发送表单中的数据,地址可以是相对地址也可以是绝对地址--><!--method将表单中的数据传送给服务器处理,get方式直接显示在url地址中,数据可以被缓存,且长度有限制;而post方式数据隐藏传输,_html表单的处理程序有那些

PHP设置谷歌验证器(Google Authenticator)实现操作二步验证_php otp 验证器-程序员宅基地

文章浏览阅读1.2k次。使用说明:开启Google的登陆二步验证(即Google Authenticator服务)后用户登陆时需要输入额外由手机客户端生成的一次性密码。实现Google Authenticator功能需要服务器端和客户端的支持。服务器端负责密钥的生成、验证一次性密码是否正确。客户端记录密钥后生成一次性密码。下载谷歌验证类库文件放到项目合适位置(我这边放在项目Vender下面)https://github.com/PHPGangsta/GoogleAuthenticatorPHP代码示例://引入谷_php otp 验证器

【Python】matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距-程序员宅基地

文章浏览阅读4.3k次,点赞5次,收藏11次。matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距

docker — 容器存储_docker 保存容器-程序员宅基地

文章浏览阅读2.2k次。①Storage driver 处理各镜像层及容器层的处理细节,实现了多层数据的堆叠,为用户 提供了多层数据合并后的统一视图②所有 Storage driver 都使用可堆叠图像层和写时复制(CoW)策略③docker info 命令可查看当系统上的 storage driver主要用于测试目的,不建议用于生成环境。_docker 保存容器

随便推点

网络拓扑结构_网络拓扑csdn-程序员宅基地

文章浏览阅读834次,点赞27次,收藏13次。网络拓扑结构是指计算机网络中各组件(如计算机、服务器、打印机、路由器、交换机等设备)及其连接线路在物理布局或逻辑构型上的排列形式。这种布局不仅描述了设备间的实际物理连接方式,也决定了数据在网络中流动的路径和方式。不同的网络拓扑结构影响着网络的性能、可靠性、可扩展性及管理维护的难易程度。_网络拓扑csdn

JS重写Date函数,兼容IOS系统_date.prototype 将所有 ios-程序员宅基地

文章浏览阅读1.8k次,点赞5次,收藏8次。IOS系统Date的坑要创建一个指定时间的new Date对象时,通常的做法是:new Date("2020-09-21 11:11:00")这行代码在 PC 端和安卓端都是正常的,而在 iOS 端则会提示 Invalid Date 无效日期。在IOS年月日中间的横岗许换成斜杠,也就是new Date("2020/09/21 11:11:00")通常为了兼容IOS的这个坑,需要做一些额外的特殊处理,笔者在开发的时候经常会忘了兼容IOS系统。所以就想试着重写Date函数,一劳永逸,避免每次ne_date.prototype 将所有 ios

如何将EXCEL表导入plsql数据库中-程序员宅基地

文章浏览阅读5.3k次。方法一:用PLSQL Developer工具。 1 在PLSQL Developer的sql window里输入select * from test for update; 2 按F8执行 3 打开锁, 再按一下加号. 鼠标点到第一列的列头,使全列成选中状态,然后粘贴,最后commit提交即可。(前提..._excel导入pl/sql

Git常用命令速查手册-程序员宅基地

文章浏览阅读83次。Git常用命令速查手册1、初始化仓库git init2、将文件添加到仓库git add 文件名 # 将工作区的某个文件添加到暂存区 git add -u # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,不处理untracked的文件git add -A # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,包括untracked的文件...

分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120-程序员宅基地

文章浏览阅读202次。分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120

【C++缺省函数】 空类默认产生的6个类成员函数_空类默认产生哪些类成员函数-程序员宅基地

文章浏览阅读1.8k次。版权声明:转载请注明出处 http://blog.csdn.net/irean_lau。目录(?)[+]1、缺省构造函数。2、缺省拷贝构造函数。3、 缺省析构函数。4、缺省赋值运算符。5、缺省取址运算符。6、 缺省取址运算符 const。[cpp] view plain copy_空类默认产生哪些类成员函数

推荐文章

热门文章

相关标签