ACE介绍
一. 摘要 (3)
二. 适用者 (3)
三. 开始 (3)
3.1 main (3)
3.2 环境初始化 (3)
3.3 日志 (4)
四. 内存使用 (7)
2.1 缓存内存分配类ACE_Cached_Allocator (7)
2.2 内存分配器ACE_Malloc (8)
2.3 内存映射:ACE_Mem_Map ............... 错误!未定义书签。
2.4 内存池:ACE_XXX_Memory_Pool ...... 错误!未定义书签。
2.5 共享内存:ACE_Shared_Memory_MM 错误!未定义书签。
五. 锁 (10)
3.1 原子锁:ACE_Atomic_Op (12)
3.2 其他锁:ACE_XXX_Mutex (15)
六. 队列 (16)
3.3 线性队列:ACE_Unbounded_Set (16)
3.4 内存块模型:ACE_Data_Block (17)
3.5 消息存储模型:ACE_Message_Block (18)
3.6 消息队列:ACE_Message_Queue_Ex (19)
七. 线程 (10)
5.1 线程管理类:ACE_Thread_Manager (10)
八. 任务:ACE_Task (21)
3.1 机制............................................. 错误!未定义书签。
3.2 (27)
九. 网络通讯............................................ 错误!未定义书签。
十. 反应器:ACE::Reactor (40)
十一. ACE常用设计模式 (47)
9.1 单件模式之应用:Singleton (47)
9.2 适配器模式之应用:Adapter (47)
9.3 迭代器模式之应用:Iterator (48)
9.4 置换模式之应用:Substitution ........... 错误!未定义书签。
9.5 工厂模式....................................... 错误!未定义书签。十二. 主动对象介绍 ...................................... 错误!未定义书签。十三. 参考文献............................................ 错误!未定义书签。
一.摘要
ACE(自适配通信环境:Adaptive Communication Environment),封装了OS层的底层API,并结合设计模式为各种操作平台提供了一整套高性能的底层开发包。开发包的内容涉及到:进程间通信、内存管理、线程管理、同步、网络通信、事件分发、服务器配置等。
二.适合阅读者
本文档适用ACE初学者,主要介绍如何利用ACE封装的优势来开发我们自己应用系统。
三.开始
3.1main
ACE对main做了重定义,目的是为了适应不同平台的编译入口点,这个一般不会影响到我们的开放过程,我们可以象平时那样按照main的格式写我们的入口函数:int main(int argv, char *argc[]);,main函数的两个参数一定要写,如果这样写:int main()会出现编译错误:unresolved external symbol "int __cdecl ace_main_i(int,char * * const)"。
我们当然也可以不按照ACE规定的main格式,只要在main函数前面增加下面的语句就可以了:
#ifdef main
#undef main
#endif
3.2环境初始化
如果使用ACE类库提供的服务时例如:反应器(Reactor),需要在应用系统
的开始和结束处各添加ACE::init()和ACE::fini(),分别用来初始化ACE环境和释放资源,另外对于单实例模式的使用也需要使用ACE::init和ACE::fini。这两个静态函数其实是调用ACE_Object_Manager类的init和fini方法。
对于ACE提供的单实例模式类,为了统一封装并不要求用户自己释放,而是由ACE在通过ACE::fini统一在系统的结束处释放其资源。
单实例的使用:
3.3日志
ACE提供日志及跟踪信息的调试、打印管理,类ACE_Log_Msg提供日志的显示和打印,用户也可以通过继承ACE_Log_Msg_Callback类实现日志的自己管理。在我们代码里面可以使用两个宏:ACE_DEBUG和ACE_ERROR记录日志,下面例子简单显示两个日志:
int main(int argc, char * argv[])
{
ACE_DEBUG((LM_DEBUG, “ACE_DEBUG测试\n”)); //注意宏前后一定要使用两个括号
}
和日志相关的三个头文件:log_msg.h、log_record.h、log_msg_callback.h 使用ACE_Log_Msg实现日志管理,默认情况下ACE_Log_Msg根据flags输出日志,默认的flags是ACE_Log_Msg::stderr,例如上面的例子日志被输出到console,我们可以通过修改flags改变日志输出的位置。
1、作为系统日志输出,下面的代码可以输出成系统日志,通过WINDOWS事件
查看器可以看到结果。
#include "ace/log_msg.h"
#include "ace/log_msg_callback.h"
#include "ace/log_record.h"
int main (int argc, ACE_TCHAR * argv[])
{
//ACE_LOG_MSG宏其实是ACE_Log_Msg::instance()
ACE_LOG_MSG->open("logtest", ACE_Log_Msg::SYSLOG);
ACE_DEBUG((LM_INFO, "日志测试11\n"));
}
2、输出到文件
#include "ace/Os.h"
#include "ace/log_msg.h"
#include "ace/log_msg_callback.h"
#include "ace/log_record.h"
#include
using namespace std;
int main (int argc, ACE_TCHAR * argv[])
{
ACE_DEBUG((LM_DEBUG, "first message\n"));
//清除原来的标志,如果不清除在输出到文件到同时也会在console上输出。
ACE_LOG_MSG->clr_flags(ACE_Log_Msg::STDERR);
ACE_DEBUG((LM_DEBUG, "second message\n"));
ACE_LOG_MSG->set_flags(ACE_Log_Msg::OSTREAM);
ACE_DEBUG((LM_DEBUG, "third message\n"));
//定义文件名,并设置文件句柄
const char * filename="main.log";
ofstream out(filename, ios::out | ios::trunc);
ACE_LOG_MSG->msg_ostream(&out);
//测试信息,这次能真正输出到文件。
ACE_DEBUG((LM_DEBUG, "forth message\n"));
return 0;
}
3、自己处理日志
#include "ace/Os.h"
#include "ace/log_msg.h"
#include "ace/log_msg_callback.h"
#include "ace/log_record.h"
/********************************************
* 自己处理日志类
*******************************************/
class CLogger : public ACE_Log_Msg_Callback
{
public:
void log (ACE_Log_Record & record); //重定义虚函数
};
void
CLogger::log(ACE_Log_Record & record)
{
//打印日志
ACE_OS::printf("%s\n", record.msg_data());
}
int main (int argc, ACE_TCHAR * argv[])
{
ACE_DEBUG((LM_DEBUG, "first message\n"));
ACE_LOG_MSG->clr_flags(ACE_Log_Msg::STDERR);
ACE_DEBUG((LM_DEBUG, "second message\n"));
ACE_LOG_MSG->set_flags(ACE_Log_Msg::MSG_CALLBACK);
ACE_DEBUG((LM_DEBUG, "third message\n"));
//设置自定义类
CLogger logger;
ACE_LOG_MSG->msg_callback(&logger);
ACE_DEBUG((LM_DEBUG, "forth message\n"));
return 0;
}
四.内存使用
ACE的内存管理类常用的就是两个,一个是ACE_Cached_Allocator,一个是ACE_Malloc,这两个类都可以和锁相结合完成多线程中内存申请同步问题,下面介绍这两个类。
2.1缓存内存分配类ACE_Cached_Allocator
这个类在构造函数中通过new操作预先分配一定的内存,并按照第一个模板参数的大小对内存进行分块把每一块的内存偏移指针作为链表的节点存放在成员变量ACE_Locked_Free_List中,在析构时释放已申请的内存。
类的定义:ACE_Cached_Allocator
用户常使用方法有malloc()、calloc()、free(),用户在调用这几个函数申请内存时,ACE_Cached_Allocator只是每次从链表中返回一个节点,用户每次申请内存的大小不能超过sizeof(T),malloc和calloc不同之处是calloc根据第二个参数对内存初始化。
下面为使用例子程序:
#include "ace/malloc.h"
struct SNode
{
int n;
};
int main (int argc, ACE_TCHAR * argv[])
{
ACE_Cached_Allocator
allocator(64);
ACE_Cached_Allocator
char * p = (char*)allocator.malloc(32);
if (!p) {
ACE_DEBUG((LM_DEBUG, "申请内存为空\n"));
}
SNode * node = (SNode*)stu_allocator.malloc(sizeof(SNode));
if (!node) {
ACE_DEBUG((LM_DEBUG, "申请内存为空\n"));
} else {
ACE_DEBUG((LM_DEBUG, "val:%d\n", node->n));
stu_allocator.free(node);
}
if ( node = (SNode*)stu_allocator.calloc(sizeof(SNode)) ) { ACE_DEBUG((LM_DEBUG, "val:%d\n", node->n));
}
allocator.free(p);
stu_allocator.free(node);
return 0;
}
2.2内存分配器ACE_Malloc
和ACE_Cached_Allocator不同的是,ACE_Malloc提供几个内存池类型,通过指定不同的内存池类型可以实现进程间内存共享,满足进程间内存共享的类型包括:ACE_MMAP_Memory_Pool、ACE_Shared_Memory_Pool、ACE_Local_Memory_Pool。
ACE_Malloc只是根据第一个模板参数调用不同的内存池实现内存分配,在实现跨进程内存上ACE_Malloc通过一个名称与每次申请的内存块进行绑定,在进程内的使用函数有malloc、free,进程间多了bind、find函数,下面先演示进程内使用:
#include "ace/os.h"
#include "ace/local_memory_pool.h"
#include "ace/Synch.h"
#include "ace/malloc.h"
#include "ace/malloc_t.h"
typedef ACE_Malloc
struct SNode
{
int n;
};
int main(int argc, char * argv[])
{
CMemMalloc memMalloc;
SNode * ptrNode = (SNode*)memMalloc.malloc ( sizeof(SNode) );
if ( NULL == ptrNode ) {
ACE_DEBUG ((LM_DEBUG, "申请内存失败\n"));
} else {
memMalloc.free (ptrNode);
}
return 0;
}
下面演示进程间使用:
5.1简单的线程类ACE_Thread
ACE_Thread实现了跨平台的线程封装,启动线程函数为静态函数ACE_Thread::spawn_n,通过join函数保证线程释放资源,按照OOP封装的原则并不建议用户过多的使用ACE_Thread,而应该采用后面介绍的ACE_Task,下面为简单的演示代码:
#include "ace/os.h"
#include "ace/log_msg.h"
#include "ace/thread.h"
#include "ace/guard_t.h"
#include "ace/thread_mutex.h"
static void Worker(void * arg)
{
ACE_DEBUG((LM_DEBUG, "线程测试\n"));
}
int main(int argc, char ** argv)
{
ACE_thread_t idThread;
ACE_hthread_t hThread;
if (-1 == ACE_Thread::spawn_n (&idThread,
1,
(ACE_THR_FUNC)Worker,
0,
THR_JOINABLE | THR_NEW_LWP,
ACE_DEFAULT_THREAD_PRIORITY,
0,
&hThread)) {
ACE_DEBUG((LM_DEBUG, "starting threads failed\n"));
return -1;
}
getchar();
ACE_Thread::join(hThread);
return 0;
}
5.2高级线程管理类:ACE_Thread_Manager
ACE_Thread_Manager是ACE_Thread的超集,增加了线程管理功能:启动、挂起、恢复、取消等;后面介绍的ACE_Task就是采用ACE_Thread_Manager 管理自己的线程池。修改上面的代码如下:
if ( -1 == ACE_Thread_Manager::instance()->spawn_n(
1,
(ACE_THR_FUNC)Worker,
0,
THR_NEW_LWP,
ACE_DEFAULT_THREAD_PRIORITY,
1)) {
ACE_ERROR((LM_ERROR, "Failure to spawn first group of threads: %p \n"));
return -1;
}
getchar();
ACE_Thread_Manager::instance()->wait_grp(1);
六.锁
3.1线程锁ACE_Thread_Mutex
线程锁在WIN32下为临界区CRITICAL_SECTION,其他平台下调用pthread_mutex_lock实现了跨平台的通用锁,通过锁的函数为acquire并可以传递参数设置超时,当acquire超时时返回-1并且errno等于ETIME,tryacquire 函数是一个测试是否可以获取锁的函数-1失败并设置errno为EBUSY,释放锁函数release,下面为使用方法:
#include "ace/os.h"
#include "ace/log_msg.h"
#include "ace/thread.h"
#include "ace/thread_mutex.h"
#define THREAD_NUMS 2
class CMutexTest
{
public:
CMutexTest():_bRun(0){}
int Open ();
void Close ();
private:
friend void Worker(void *arg);
private:
bool_bRun;
ACE_Thread_Mutex _Mutex;
ACE_thread_t _ThreadIds[THREAD_NUMS];
ACE_hthread_t _ThreadHandle[THREAD_NUMS];
};
int CMutexTest::Open ()
{
if (-1 == ACE_Thread::spawn_n (_ThreadIds,
THREAD_NUMS,
(ACE_THR_FUNC)Worker,
this,
THR_JOINABLE | THR_NEW_LWP,
ACE_DEFAULT_THREAD_PRIORITY,
0,
0,
_ThreadHandle)) {
ACE_DEBUG((LM_DEBUG, "starting threads failed\n"));
return -1;
}
_bRun = 1;
return 0;
}
void Worker(void * arg)
{
CMutexTest * pMutex = (CMutexTest*)arg;
ACE_Time_Value t(0, 10);
for (int i = 0; i < THREAD_NUMS; i++ )
if (ACE_Thread::self() == pMutex->_ThreadIds[i] ) { while (pMutex->_bRun ) {
pMutex->_Mutex.acquire ();
ACE_DEBUG((LM_DEBUG, "enter thread:%d\n", pMutex->_ThreadIds[i]));
pMutex->_Mutex.release ();
ACE_OS::sleep(t);
}
ACE_DEBUG((LM_DEBUG, "exit thread:%d\n",
pMutex->_ThreadIds[i]));
}
}
void CMutexTest::Close()
{
_bRun = 0;
for (int i = 0; i < THREAD_NUMS; i++)
ACE_Thread::join (_ThreadHandle[i]);
}
int main (int argc, char * argv[])
{
CMutexTest mutex;
if (mutex.Open () != -1) {
getchar ();
mutex.Close ();
}
return 0;
}
3.2读写线程锁ACE_RW_Thread_Mutex
读写锁的机制是可允许多个线程同时对一个数据进行“读”操作,一个“写”操作和多个“读”操作之间是互斥的。WIN32下没有提供专门的读写锁,在*NIX下有对应的读写锁;操作函数有:acquire_write、acquire_read、release。
3.3原子锁:ACE_Atomic_Op
定义为template<>class ACE_Export
ACE_Atomic_Op
例如:
ACE_Atomic_Op val;
val++; //递增
val--; //递减
int I = val.value(); //返回当前值
val = 5; //可以直接赋值
3.4锁守护:ACE_Guard
为了防止程序员忘记释放锁,往往通过类的构造和析构函数特性实现自动加锁和释放锁,在ACE里面也提供这样的机制ACE_Guard,使用方法如下:
void Worker(void * arg)
{
CMutexTest * pMutex = (CMutexTest*)arg;
ACE_Time_Value t(0, 10);
for (int i = 0; i < THREAD_NUMS; i++ )
if (ACE_Thread::self() == pMutex->_ThreadIds[i] ) {
while (pMutex->_bRun ) {
ACE_Guard
(pMutex->_Mutex);
ACE_DEBUG((LM_DEBUG, "enter thread:%d\n", pMutex->_ThreadIds[i]));
ACE_OS::sleep(t);
}
ACE_DEBUG((LM_DEBUG, "exit thread:%d\n",
pMutex->_ThreadIds[i]));
}
}
七.队列
3.5线性队列:ACE_Unbounded_Set
ACE_Unbounded_Set使用ACE_NODE
Insert:向队列添加一个数据成员,队列通过malloc构造成员。
Find:在队列查找指定的成员,0成功,-1失败。
Remove:在队列移出指定的成员。0成功,-1失败
Begin:返回队列的开始节点。
End:返回队列的尾节点
Copy_node:把一个队列的所有节点添加到本队列的末尾。
Delete_node:删除本队列的所有节点。
ACE_Unbounded_Set没有提供锁保护用户在外部使用时需要采用合适的锁。
例子:
int main(int argv, char * argc[])
{
ACE_Unbounded_Set
ACE_Unbounded_Set_Iterator
for (int i = 0; i < 10; i++)
liner.insert(i*100);
printf("size of liner:%d\n", liner.size());
for (it = liner.begin (); it != liner.end(); it++)
printf("%d ", *it);
liner.remove(500);
printf("\nafter remove 500:\n");
for (it = liner.begin (); it != liner.end(); it++)
printf("%d ", *it);
if (liner.find(300) == 0)
printf("\nthe element 300 has been found:\n");
return 0;
}
3.6内存块模型:ACE_Data_Block
ACE_Data_Block功能简单描述就是通过锁策略和共享引用计数管理一块内存。提供两个内存分配接口allocator_strategy_和data_block_allocator_,外部的使用者例如ACE_Message_Block可以利用ACE_Data_Block实现数据管理。ACE_Data_Block的构造函数定义如下:
ACE_Data_Block::ACE_Data_Block (void); //缺省的构造函数
ACE_Data_Block::ACE_Data_Block (size_t size,
ACE_Message_Block::ACE_Message_Type msg_type,
const char *msg_data,
ACE_Allocator *allocator_strategy,
ACE_Lock *locking_strategy,
ACE_Message_Block::Message_Flags flags,
ACE_Allocator
*data_block_allocator);
下面是常用函数的演示程序:
int main(int argv, char * argc[])
{
ACE_Lock_Adapter
ACE_Data_Block block(32, ACE_Message_Block::MB_DATA, 0, 0, &mutex, 0, 0);
ACE_Data_Block * p_b, *p_b2;
int i;
printf("size of block:%d\n", block.size());
printf("capacity of block:%d\n", block.capacity());
int * p = (int*)block.base();
for (i = 0; i < block.size()/4; i++)
*p++ = i*100;
p_b = block.duplicate();
p = (int*)p_b->base();
for (i = 0; i < p_b->size()/4; i++)
printf("%d ", *p++);
if (p_b == &block && p_b->base() == block.base())
printf("the two blocks is equal\n");
p_b2 = block.clone(0);
p = (int *)p_b2->base();
for (i = 0; i < p_b2->size()/4; i++)
printf("%d ", *p++);
if (p_b2 != p_b && p_b2->base() != p_b->base())
printf("the two blocks is not equal\n");
p_b2->release();
return 0;
}
3.7消息存储模型:ACE_Message_Block
ACE_Message_Block的作用是在ACE_Data_Block基础上进行数据管理,可以在不同的内存块标注不同的数据类型,提供数据的读取和写入操作,通过prev 和next指针实现Double Link。ACE_Message_Block主要为ACE_Message_Block队列提供底层机制。
//简单初始化创建空消息
ACE_Message_Block (ACE_Allocator *message_block_allocator = 0);
ACE_Message_Block::ACE_Message_Block (const char *data,
size_t size,
unsigned long priority)
3.8消息队列:ACE_Message_Queue_Ex
ACE_Message_Queue_Ex是一个功能更强的消息队列,把ACE_Message_Queue作为成员变量实现更丰富的功能,使用者在外面调用enqueue_tail、dequeue_head等函数时不再象ACE_Message_Queue那样需要传递ACE_Message_Block指针,从而实现了更好的封装。
几行最简单的代码:
#include "ace/os.h"
#include "ace/condition_thread_mutex.h"
#include "ace/thread.h"
#include "ace/message_queue_t.h"
struct SNode
{
SNode(int m) : _n(m){}
int _n;
};
typedef ACE_Message_Queue_Ex
int main(int argc, char ** argv)
{
MessageQueue msgQueue;
SNode * pNode = NULL;
for (int i = 0; i < 10; i++)
msgQueue.enqueue_tail(new SNode(i));
while (msgQueue.message_count()) {
msgQueue.dequeue_head(pNode);
if (pNode) {
ACE_DEBUG((LM_DEBUG, "Node value:%d\n",
pNode->_n));
delete pNode;
}
}
return 0;
}
水位的作用
在ACE_Message_Queue_Ex队列的构造函数或者open函数中需要传递两个水位标志(低水位标志和高水位标志),用于在流中的相邻Module间实现层到层的流控制。高水位标指示消息队列在进行流控制之前所愿意缓存的消息字节数。低水位标指示在先前已进行流控制的Message Queue不再被视为满的“水位”。