很多人在Windows使用ACE的时候往往会出现以下的Link错误。
Why do I get errors while using 'TryEnterCriticalSection'?
\ace/OS.i(2384) : error C2039:
'TryEnterCriticalSection': is not a member of '`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的头文件包含定义放在在顶部,这样也可以避免这个编译错误。
预定义头文件是一个良好的编程习惯,你可以将自己的大部分宏定义,include包含的本工程以外的外部.h文件。简言之就是预定义头文件中使用#include<>,表示包含工程以外文件,自己工程内部只使用#include””,表示包含当前工程目录下的文件。大部分C/C++的程序员都有过链接和一些预定义冲突错误消耗大量的时间,原来我也是如此,但是在掌握预定义头文件方法后,我几乎没有为这个问题折磨过。其实Virsual C++ 在生产MFC工程的时候,会自动帮你自动生产一个预定义头文件stdafx.h,只是我们不善利用而已。
ACE有一个非常优美的定时器队列模型,他提供了4种定时器Queue让大家使用:ACE_Timer_Heap,ACE_Timer_Wheel,ACE_High_Res_Timer,ACE_Timer_Hash。在《C++ Network Programming Volume 2 - Systematic Reuse with ACE and Frameworks》中间有相应的说明,其中按照说明最诱人的的是:
ACE_Timer_Hash, which uses a hash table to manage the queue. Like the timing wheel implementation, the average-case time required to schedule, cancel, and expire timers is O(1) and its worst-case is O(n).
但是遗憾的是,ACE_Timer_Hash其实是性能最差的。几乎不值得使用。我曾经也被诱惑过,但是在测试中间发现,文档中所述根本不属实,在一个大规模定时器的程序中,我使用ACE_Timer_Hash发现性能非常不理想,检查后发现ACE的源代码如下:
template <class TYPE, class FUNCTOR, class ACE_LOCK, class BUCKET> int
ACE_Timer_Hash_T<TYPE, FUNCTOR, ACE_LOCK, BUCKET>::expire (const ACE_Time_Value &cur_time)
{
// table_size_为Hash的桶尺寸,如果要避免冲突,桶的数量应该尽量大,
//每个桶可以理解为一个Hash开链的链表
// Go through the table and expire anything that can be expired
//遍历所有的桶
for (size_t i = 0;
i < this->table_size_;
++i)
{
//在每个桶中检查是否有要进行超时处理的元素
while (!this->table_[ i]->is_empty ()
&& this->table_[ i]->earliest_time () <= cur_time)
{
…………
这个问题在ACE自己的文档[url=http://www.cs.wustl.edu/%7Eschmidt/Timer_Queue.html]《Design, Performance, and Optimization of Timer Strategies for Real-time ORBs》中间也有较为正确的描述。
WFMO_Reactor 选择的是Windows的WSAEventSelect 函数作为网络的IO的反应器。但是WSAEventSelect函数的FD_WRITE的事件处理和传统的IO反应器(select)不同。下面是MSDN的描述。
The FD_WRITE network event is handled slightly differently. An FD_WRITE network event is recorded when a socket is first connected with connect/WSAConnect or accepted with accept/WSAAccept, and then after a send fails with WSAEWOULDBLOCK and buffer space becomes available. Therefore, an application can assume that sends are possible starting from the first FD_WRITE network event setting and lasting until a send returns WSAEWOULDBLOCK. After such a failure, the application will find out that sends are again possible when an FD_WRITE network event is recorded and the associated event object is set.
先选择最常用的Time_Queue ACE_Timer_Heap举例,其使用ACE_Event_Handler关闭定时器的代码是:
template <class TYPE, class FUNCTOR, class ACE_LOCK> int
ACE_Timer_Heap_T<TYPE, FUNCTOR, ACE_LOCK>::cancel (const TYPE &type,
int dont_call)
{
// Try to locate the ACE_Timer_Node that matches the timer_id.
//循环比较所有的的ACE_Event_Handler的指针是否相同
for (size_t i = 0; i < this->cur_size_; )
{
if (this->heap_[ i]->get_type () == type)
{
………………
}
}
而使用TIMER_ID关闭的代码如下,它是通过数组下标进行的定位操作。
template <class TYPE, class FUNCTOR, class ACE_LOCK> int
ACE_Timer_Heap_T<TYPE, FUNCTOR, ACE_LOCK>::cancel (long timer_id,
const void **act,
int dont_call)
{
//通过数组下标操作,速度当然奇快无比。
ssize_t timer_node_slot = this->timer_ids_[timer_id];
……
//跟进数组ID进行操作
else
{
ACE_Timer_Node_T<TYPE> *temp =
this->remove (timer_node_slot);
}
}