6.4.3 使用不同的定时器队列
不同的环境可能需要不同的调度和取消定时器的方法。在下面的任一条件为真时,实现定时器的算法的性能就会成为一个问题:
ACE
允许用户从若干在ACE中已存在的定时器中进行选择,或是根据为定时器定义的接口开发他们自己的定时器。表6-3详细列出了ACE中可用的各种定时器:
|
定时器 |
数据结构描述 |
性能 |
|
ACE_Timer_Heap |
定时器存储在优先级队列的堆实现中。 |
schedule_timer() 的开销=O(lg n)cancel_timer() 的开销=O(lg n)查找当前定时器的开销 =O(1) |
|
ACE_Timer_List |
定时器存储在双向链表中。 |
schedule_timer() 的开销=O(n)cancel_timer() 的开销=O(1)查找当前定时器的开销 =O(1) |
|
ACE_Timer_Hash |
在这里使用的这种结构是定时器轮算法的变种。性能高度依赖于所用的哈希函数。 |
schedule_timer() 的开销=最坏=O(n) 最佳=O(1)cancel_timer() 的开销=O(1)查找当前定时器的开销 =O(1) |
|
ACE_Timer_Wheel |
定时器存储在“数组指针” (pointers to arrays)的数组中。每个被指向的数组都已排序。 |
schedule_timer() 的开销=最坏=O(n)cancel_timer() 的开销=O(1)查找当前定时器的开销 =O(1) |
更多有关定时器的信息见参考文献
[VII]
表
6-3 ACE中的定时器处理信号(Signal)
如我们在例
6-1中所看到的,反应堆含有进行信号处理的方法。处理信号的事件处理器应重载handle_signal()方法,因为该方法将在信号发生时被回调。要为信号登记处理器,可以使用多个register_handler()方法中的一个,就如同例6-1中所演示的那样。如果对特定信号不再感兴趣,通过调用remove_handler(),处理器可以被拆除,并恢复为先前安装的信号处理器。反应堆在内部使用sigaction()系统调用来设置和恢复信号处理器。通过使用ACE_Sig_Handlers类和与其相关联的方法,无需反应堆也可以进行信号处理。使用反应堆进行信号处理和使用
ACE_Sig_Handlers类的重要区别是基于反应堆的机制只允许应用给每个信号关联一个事件处理器,而ACE_Sig_Handlers类允许在信号发生时,回调多个事件处理器。使用通知(Notification)
反应堆不仅可以在系统事件发生时发出回调,也可以在用户定义的事件发生时回调处理器。这是通过反应堆的“通知”接口来完成的;该接口由两个方法组成:
notify()和max_notify_iterations()。通过使用
notify()方法,可以明确地指示反应堆对特定的事件处理器对象发出回调。在反应堆与消息队列、或是协作任务协同使用时,这是十分有用的。可在ASX构架组件与反应堆一起使用时找到这种用法的一些好例子。max_notify_iterations()
方法通知反应堆,每次只完成指定次数的“迭代”(iterations)。也就是说,在一次handle_events()调用中只处理指定数目的“通知”。因而如果使用max_notify_iterations()将迭代的次数设置为20,而又有25个通知同时到达,handle_events()方法一次将只处理这些通知中的20个。剩下的五个通知将在handle_events()下一次在事件循环中被调用时再处理。下面的例子将进一步阐释这些概念:
例
6-4#include ”ace/Reactor.h”
#include ”ace/Event_Handler.h”
#include ”ace/Synch_T.h”
#include ”ace/Thread_Manager.h”
#define WAIT_TIME 1
#define SLEEP_TIME 2
class My_Handler: public ACE_Event_Handler
{
public:
//Start the event handling process.
My_Handler()
{
ACE_DEBUG((LM_DEBUG,”Event Handler created\n”));
ACE_Reactor::instance()->max_notify_iterations(5);
return 0;
}
//Perform the notifications i.e., notify the reactor 10 times
void perform_notifications()
{
for(int i=0;i<10;i++)
ACE_Reactor::instance()->
notify(this,ACE_Event_Handler::READ_MASK);
}
//The actual handler which in this case will handle the notifications
int handle_input(int)
{
ACE_DEBUG((LM_DEBUG,”Got notification # %d\n”,no));
no++;
return 0;
}
private:
static int no;
};
//Static members
int My_Handler::no=1;
int main(int argc, char *argv[])
{
ACE_DEBUG((LM_DEBUG,”Starting test \n”));
//Instantiating the handler
My_Handler handler;
//The done flag is set to not done yet.
int done=0;
while(1)
{
//After WAIT_TIME the handle_events will fall through if no events
//arrive.
ACE_Reactor::instance()->handle_events(ACE_Time_Value(WAIT_TIME));
if(!done)
{
handler.perform_notifications();
done=1;
}
sleep(SLEEP_TIME);
}
}
上面的例子和平常一样创建了具体处理器、并重载了
handle_input()方法;这和我们需要我们的处理器对来自I/O设备的输入数据进行处理时是一样的。处理器还含有open()方法(用于执行处理器初始化)和实际完成通知的方法。在
main()函数中,我们首先实例化我们的具体处理器的一个实例。通过使用反应堆的max_notify_iterations()方法,处理器的构造器保证max_notify_iterations被设置为5。在此之后,反应堆的事件处理循环开始了。在这里,事件处理循环中值得注意的一个主要区别是,程序传递给
handle_events()一个ACE_Time_Value。如果在此时间内没有事件发生,handle_events()方法就会结束。在handle_events()结束后,perform_notification()被调用,它使用反应堆的notify()方法来请求反应堆通知处理器(它是在事件发生时被作为参数传入的)。随后反应堆就使用所收到的掩码来执行对处理器的适当“handle”方法的调用。在此例中,通过传递ACE_Event_Handler::READ_MASK,我们使用notify()来通知我们的事件处理器有输入,从而使得反应堆回调该处理器的handle_input()方法。因为我们已将
max_notify_iterations设为5,所以在一次handle_events()调用过程中反应堆实际上只会发出5个通知。为说明这一点,在发出下一个handle_events()调用前,我们使反应事件循环停止,时间为SLEEP_TIME。上面的例子过于简单,也非常不实际,因为通知发生的线程和反应堆所在的线程是同一线程。更为实际的例子是:事件发生在另一线程中,并将这些事件通知反应堆线程。下面所演示的是同一个例子,不过是由不同的线程来执行通知:
例
6-5#include ”ace/Reactor.h”
#include ”ace/Event_Handler.h”
#include ”ace/Synch_T.h”
#include ”ace/Thread_Manager.h”
class My_Handler: public ACE_Event_Handler
{
public:
//Start the event handling process.
My_Handler()
{
ACE_DEBUG((LM_DEBUG,”Got open\n”));
activate_threads();
ACE_Reactor::instance()->max_notify_iterations(5);
return 0;
}
//Spawn a separate thread so that it notifies the reactor
void activate_threads()
{
ACE_Thread_Manager::instance()
->spawn((ACE_THR_FUNC)svc_start,(void*)this);
}
//Notify the Reactor 10 times.
void svc()
{
for(int i=0;i<10;i++)
ACE_Reactor::instance()->
notify(this, ACE_Event_Handler::READ_MASK);
}
//The actual handler which in this case will handle the notifications
int handle_input(int)
{
ACE_DEBUG((LM_DEBUG, ”Got notification # %d\n”, no));
no++;
return 0;
}
//The entry point for the new thread that is to be created.
static int svc_start(void* arg);
private:
static int no;
};
//Static members
int My_Handler::no=1;
int My_Handler::svc_start(void* arg)
{
My_Handler *eh= (My_Handler*)arg;
eh->svc();
return -1; //de-register from the reactor
}
int main(int argc, char *argv[])
{
ACE_DEBUG((LM_DEBUG,”Starting test \n”));
My_Handler handler;
while(1)
{
ACE_Reactor::instance()->handle_events();
sleep(3);
}
}
这个例子和前一个例子非常相像,除了增加了一些方法来派生线程,并随即在事件处理器中启用线程。特别地,具体处理器
My_Handler的构造器调用启用方法,该方法使用ACE_Thread_Manager::spawn()方法来派生一个分离的线程,并将svc_start()作为它的入口。svc_start()
方法调用perform_notifications()来将通知发送给反应堆,但这一次它们是从新线程、而不是反应堆所在的线程发送的。注意该线程的入口svc_start()被定义为静态方法(它随后调用非静态的svc()方法)。这是线程库的使用要求,也就是,线程的入口必须是文件范围内的静态函数。