ACE问题总结

一、  ACE的链接Link错误

很多人在Windows使用ACE的时候往往会出现以下的Link错误。

\ace/OS.i(2384) : error C2039:

'TryEnterCriticalSection': is not a memberof '`global namespace''

其实这个错误不是由于ACE导致的,只是编译器把这个赃栽倒了ACE上。出现这个错误的原因主要是因为一些关键宏定义冲突,一般是_WIN32_WINNT,'TryEnterCriticalSection' 这个函数是NT4.0后才出现的函数,如果这个宏被定义的小于0x0400或者没有定义,那么就会出现这个错误。

所以最简单的处理方法是在自己的预定义头文件中加入一行。

#if !defined (_WIN32_WINNT)

# define _WIN32_WINNT 0x0400

#endif

其实ACE自己对于宏的处理是比较严谨的,ACE的config-win32-common.h中间就有这行定义,所以在一般而言,可以将ACE的头文件包含定义放在在顶部,这样也可以避免这个编译错误。

二、  Event_Handler在程序退出前应该自己关闭

在程序退出的【注】,我们往往不会自己关闭Event_Handler,而寄希望Reactor 的清理。但是实际情况会复杂很多。使用的时候必须当心。

 

【注】是否要在退出的时候清理所有分配的内存?在普通的操作系统中,程序的退出会回收所有的分配内存。所以很多人会逃避在最后阶段的清理分配的内存。但是这实在不是一个良好的习惯。一方面对于很多OS(比如嵌入系统)不会回收内存资源,一些内核资源(UNIX)也不会在进程退出后释放,编程就应该要养成清理的好习惯,更何况不进行释放在内存检查的软件一般会报错,如果不清理会干扰我们对于内存泄露的定位。

三、  ACE_SOCK_Stream不会在析构关闭

有OO基础的程序都会放资源的释放放入析构中间去。所以我看到ACE_SOCK_Stream也以为他的在析构中关闭Socket的句柄,但是事实是ACE_SOCK_Stream必须自己显式调用close函数关闭Socket句柄。

当然,这倒不是ACE的设计缺陷,而是ACE的ACE_SOCK_Stream是一个可以出现在堆栈,可以作为参数传递,进行赋值的类,如果在析构中关闭,就无法实现这些功能了。

实现决定设计。辨证呀。

四、  handle_events函数的ACE_Time_Value参数

Reactor的handle_events参数里面的有一个ACE_Time_Value参数,注意这个参数是一个传入传出参数。

 virtual int handle_events (ACE_Time_Value&max_wait_time);

由于Reactor内部同时要管理定时器和IO句柄,所以ACE很可能不能等待你制定的时间长度,所以他会在传出参数告诉你剩余的等待时间。这时你可以让ACE继续等待剩余时间。但在主循环处理中,你不能这样做,因为经过多次调用后,ACE_Time_Value参数会变成0(ACE_Time_Value::zero)。这是会导致hanlde_events空转,会导致CPU占用率很高。

对于大部分主循环的程序,都不需要这样做,而应该重新制定一个等待时间。

五、  正确理解ACE_Singleton的加锁

ACE_Singleton的模板参数是可以带一个锁参数的。

template <class TYPE, class ACE_LOCK>

class ACE_Singleton : public ACE_Cleanup

但你可能会错误理解这个锁参数的用途。

typedef ACE_Singleton<Manager,ACE_Thread_Mutex> MANAGER;

MANAGER::instance()->ProcessFunA();

初学者可能会疑惑加锁的是不是ProcessFunA,的处理被加锁了。但是实际上ACE_Singleton的锁只保护ACE_Singleton内部的指针分配和销毁不出现重入。也就是保护instance函数内部的指针分配和释放部分。代码剖析如下:

template <class TYPE, class ACE_LOCK>TYPE *

ACE_Singleton<TYPE,ACE_LOCK>::instance (void)

{

         //加锁部分的代码,使用GUARD方式保护new

         ACE_GUARD_RETURN (ACE_LOCK, ace_mon, *lock, 0);

         if (singleton == 0)

           {

              ACE_NEW_RETURN (singleton,(ACE_Singleton<TYPE, ACE_LOCK>), 0);

           }

 ……

 return &singleton->instance_;

}

其实理解函数栈调用的兄弟应该很容易理解这个问题,ProcessFunA 函数入栈的时候instance函数已经出栈了。instance函数内部加(解)的锁无法影响后续的调用。

 

六、  ACE_DEBUG的两层括号

这儿只是分析(猜测)一下ACE_DEBUG两层括号的来由。用习惯了Windows下面跟踪宏TRACE的人开始用ACE的调试宏ACE_DEBUG的宏都会有点不习惯,因为你必须写两层括号。

#if defined (ACE_NLOGGING)

#define ACE_DEBUG(X) do {} while (0)   /*注意ACE定义的是(X)*/

#else

#define ACE_DEBUG(X) \

 do {\

   ACE_Log_Msg *ace___ = ACE_Log_Msg::instance (); \

   ace___->log X; \          /*注意这儿,这个奇怪的写法*/

 }while (0)

#endif

//使用实例,

ACE_DEBUG((LM_ERROR,"i=%d.\n",i++));

比较起来,对于Windows下的TRACE宏的定义如下:

#ifdef _DEBUG

#define TRACE ATLTRACE

#else

#define TRACE   __noop     /* MSVC特有的一个标识符,用于忽视后面的参数 */

#endif

而ACE_DEBUG的定义比TRACE的定义是多一层(X)的,所以你必须写两层括号,ACE实际上将内层括号的内容全部作为宏参数使用了。

我曾经对这两层括号疑惑了很久。因为我觉得可以采用其他方法绕开两个括号,(你可以写一个日志类尝试一下)

#if defined (ACE_NLOGGING)

// 直接定义为一个函数的名字,当然这儿还要改写其他的很多代码

#define Z_DEBUG ACE_Log_Msg::instance()->log  

#else

#define Z_DEBUG

#endif

这样的在没有定义ACE_NLOGGING的时候,Z_DEBUG(LM_ERROR,"i=%d.\n",i++);会被替换成,(LM_ERROR,"i=%d.\n",i++),这样也不会有任何输出效果。

直到有一次发现GCC2.9的环境下编译类似代码,GCC会对这样的代码会产生告警,我大致明白了ACE_DEBUG设计者的苦衷。只有双层括号的方法才能彻底让这行代码不起任何告警。

另外使用两层括号也有性能上的好处,大家注意代码被替换成(LM_ERROR,"i=%d.\n",i++)后,i++的代码还是要执行,在我自己测试中,即使是在GCC的O3级别的优化编译中,这样的代码也不会被优化掉。而如果采用ACE_DEBUG的设计,统一替换为do {} while (0),这行代码则必然将被优化掉。而对于MSVC的编译器,他提供一个特别的标识符__noop帮助编译器优化。

七、  关于在不同线程当中启用run_event_loop()的问题

调用线程必须在调用该函数之前使用ACE_Reactor::instance()->owner(ACE_Thread::self());来获得当前线程的控制权限才能继续的进行事件分发。用TP_Reactor不会有此问题。

八、   停止事件循环的问题

while (bStart)

     {

         iEvent = ACE_Reactor::instance()->handle_events();

     }

我们需要手动给ACE_Reactor::instance()->handle_events();

加一个循环,否则,此函数只进行一次分发就会返回。主意此函数可以设置阻塞时间,如果不设置默认为永远阻塞,所以如果你希望让此函数返回的时候需要从你的ACE_Event_HandlerD队列中移除一个对象比如使用ACE_Reactor::instance()->remove_handler,这样就可以使ACE_Reactor::instance()->handle_events();返回。

九、  考虑不周的Reactor Notify机制

这也应该是一个BUG,Reactor Notify的代码有考虑不周的地方。Notify机制的本质是提供了一条消息队列让大家有方法调用Event_handler,但是存在一种可能,在你的通知消息在消息队列的时候,Event_hanlder由于后面的处理可能已经handle_close了。但是ACE的dispatch_notify却没有考虑倒这一点(或者说考虑倒这一点也不好解决)。

ACE_Select_Reactor_Notify::dispatch_notify函数的代码。

 

int

ACE_Select_Reactor_Notify::dispatch_notify(ACE_Notification_Buffer &buffer)

{

ACE_Event_Handler *event_handler =

       buffer.eh_;

 

     bool const requires_reference_counting =

       event_handler->reference_counting_policy ().value () ==

       ACE_Event_Handler::Reference_Counting_Policy::ENABLED;

            //如果此时这个ACE_Event_Handler已经被handle_close了,也就时删除了此对象,狠容易引起崩溃。

     switch (buffer.mask_)

       {

       case ACE_Event_Handler::READ_MASK:

       case ACE_Event_Handler::ACCEPT_MASK:

         result = event_handler->handle_input (ACE_INVALID_HANDLE);

这个bug到5.6.1还没有解决。我觉得这个问题是可以解决的,ACE自己再其中可以提供一个删除对象的方法,并在这个方法中进行同步和错误的判断。

而我现在的做法是设置一个TIMER 让ACE去调用汉handle_timeout的时候再来删除所注册的对象。

十、  ACE定时器失效问题

ACEACE_Reactor::instance()->schedule_timer(this, 0, ACE_Time_Value(1)); 如果修改系统时间,比如向前调一段时间,TIMER的触发时按照设置时间的时间点往后加一段时间,所以如果设置1分钟后触发,但此时修改了系统时间向前调了1分钟,这样只有CPU时间的两分钟后才会触发。但如果系统时间向后调,则不会发生影响。

 

此文章由多篇文章综合拼接而成!

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
处理ace2005中文的代码可以分为以下几个步骤: 1. 数据预处理:首先需要对ace2005中文数据进行预处理。这包括读取数据集,清洗数据,去除噪音和无效信息等。可以使用Python中的正则表达式和字符串处理函数来实现。 2. 数据分词:对于中文文本,需要进行分词处理。可以使用中文分词工具,如jieba等,进行分词操作。分词后的结果可以作为接下来步骤的输入。 3. 特征提取:针对ace2005中文数据,可以从分词后的结果中提取各种特征。比如,可以提取词性特征、依存句法特征、实体识别特征等。这些特征可以用于后续的任务,如命名实体识别、关系抽取等。 4. 命名实体识别:在ace2005中文数据中,识别出文本中的命名实体是重要任务之一。可以使用机器学习方法(如基于条件随机场或深度学习的方法)对命名实体进行识别和分类。训练数据可以是人工标注好的样本。 5. 关系抽取:在ace2005中文数据中,关系抽取任务是指从文本中识别出实体间的关系。可以使用监督学习方法(如远程监督或神经网络方法)进行关系抽取。训练数据可以包含实体标注和关系标注。 6. 模型评估:为了评估处理ace2005中文数据的代码的性能,可以使用预留的测试数据进行评估。这可以通过计算评估指标(如准确率、召回率、F1值等)来实现。 7. 模型优化:根据评估结果,可以对处理ace2005中文数据的代码进行优化,例如调整模型参数,尝试不同的特征组合,或者使用更复杂的模型结构等。 总结:处理ace2005中文的代码主要包括数据预处理、分词、特征提取、命名实体识别、关系抽取、模型评估和模型优化等步骤。通过这些步骤,可以从ace2005中文数据中提取有用的信息,提高数据处理的性能和准确性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值