最近项目中遇到一个问题,用ace框架起的定时器,跑着跑着,它不跑了,然后我依赖定时器所创建的任务也一直挂在那里。查看系统软件打印的日志发现,是ace的ACE_Reactor在run_reactor_event_loop函数中返回了-1,reactor的时间循环结束,从而导致定时器函数handle_timeout函数无法再被触发。为了找出问题的具体原因,看了一把ace的源码,虽然到目前为止,为何run_reactor_event_loop函数返回-1的原因还没找到,但我却意外的发现了ace驱动定时器的原理(仅限于linux平台)。
利用ACE_Reactor来创建一个定时器主要是调用scheldule函数,ACE_Reactor的schedule函数会调用它在linux下得工具类ACE_Select_Reactor_T的schedule函数,ACE_Select_Reactor_T类中有一个ACE_Timer_Queue_T的成员变量,这个变量中保存着该ACE_Reactor创建的所有定时器的信息,包括定时器的事件对象(ACE_Event_Handle为基类的子类对象,这个对象中的handle_timeout函数即定时器的触发函数)、定时间隔、下次定时器被触发的时间(绝对时间)等。ACE_Reactor进行事件循环时会调用select函数阻塞等待被激活的句柄,select函数的最后一个入参是一个描叙时间的结构体,如果在结构体描叙的时间里还是没有等待到一个激活的句柄,那么函数将会返回0,退出等待。ACE_Reactor正是利用select的这个特性,即实现了IO事件循环的处理也实现定时器事件循环的处理。其具体的操作为:
1、从ACE_Timer_Queue_T队列中找出最近会被触发的时间器的触发时间
2、将上述取到的时间减去当前时间,并将其作为select的最后一个参数传入select中,
3、select函数返回后(要么有IO句柄被激活,要么到了需要运行定时器函数的时间),在ACE_Timer_queue_t中检测是否有需要被触发的定时器(触发时间已到,即将运行时间-当前时间<=0?),依次修改这些触发时间已到的定时器的下次运行的时间,并调用触发函数;在句柄集合中查找是否有激活状态的句柄,并进行处理(调用handle_*函数)
4、重复1、2、3的操作(循环)
这就是ace里面定时器的实现原理,由此可见ace的定时器还是会存在很多缺陷的,例如,如果一个ACE_Reactor中起了几个定时器,而某些定时器的触发函数需要运行较长得时间,那么必会导致一些定时器函数被触发的时间不准确。