CppExplore

一切像雾像雨又像风

  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  29 随笔 :: 0 文章 :: 280 评论 :: 0 Trackbacks

作者:CppExplore 网址:http://www.cppblog.com/CppExplore/
一、上篇文章描述。文章《定时器(一)》http://www.cppblog.com/CppExplore/archive/2008/04/02/46111.html实现了一个定时器模块,这个实现每次延时时间到都要扫描所有的定时器对象,效率低下。开始设想的时候,LIST中的定时器对象保存间隔时间段的毫秒值,导致每次延时时间到都要做“时间减少操作”直到减少到零,并且得出不需排序的结论。
二、改进。如果其中保存超时的精确时间点,而不是保存时间段,则可以在LIST中根据超时时间点对定时器对象排序,延时时间到,则从链表头扫描定时器对象,取其超时时间点与当前时间点对比,如果小于等于当前时间点,则进行超时处理,否则终止继续扫描,避免不必要的扫描操作。同时插入对象的时候插入到合适的位置,以保持链表的顺序化。
三、主要数据结构。此次容器结构选择内核数据结构中的TAILQ,因为LIST没有插入尾部操作(当要插入的定时器对象超时时间点大于所有队列中的对象的时候)。
四、新的时间类型操作。另一方面很多地方涉及到对struct timeval结构的操作,这里介绍几个对该结构进行操作的宏,都已经在系统头文件中定义,可以使用函数原型的方式理解就是如下:

timeradd(struct timeval *p1,struct timeval *p2,struct timeval *result);
timersub(struct timeval 
*p1,struct timeval *p2,struct timeval *result);
timercmp(struct timeval 
*p1,struct timeval *p2,operator op);

对struct timespec同样有timespecadd/timespecsub/timespeccmp,另外还有两者的转换宏,使用函数原型的方式理解就是:

TIMEVAL_TO_TIMESPEC(struct timeval *p1,struct timespec *result);
TIMESPEC_TO_TIMEVAL(struct timespec 
*p1,struct timeval *result);

如果系统的头文件中找不到,也可以自己实现下,明白这两个结构的细节结构,实现也很简单,这里拿timeradd举例,其它不说了。

#define    timeradd(tvp, uvp, vvp)                        \
    
do {                                \
        (vvp)
->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec;        \
        (vvp)
->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec;    \
        
if ((vvp)->tv_usec >= 1000000{            \
            (vvp)
->tv_sec++;                \
            (vvp)
->tv_usec -= 1000000;            \
        }
                            \
    }
 while (0)
五、实现。和上篇文章相比,改动比较的在add_timer_和process方法。当前add_timer_操作需要顺序扫描插入到合适的位置以保持链表的顺序。当前process的主要代码如下:
while(manager->m_state==TIMER_MANAGER_START){
    tm.tv_sec
=manager->m_interval.tv_sec;
    tm.tv_usec
=manager->m_interval.tv_usec;
    
while(select(0,0,0,0,&tm)<0&&errno==EINTR);
    gettimeofday(
&now,0);
/*加上误差补偿时间*/
    timeradd(
&now,&manager->m_repair,&stand);
    pthread_mutex_lock(
&manager->m_mutex);
    TAILQ_FOREACH(item, 
&(manager->list_), entry_){
/*取修正后的时间逐个和定时器中的超时时间点相比,遇到不超时对象则退出扫描*/
        
if(timercmp(&item->m_endtime,&stand,<)){
                
if(item->m_func)
                        item
->m_func(item,item->m_data);
             
if(item->m_type==CTimer::TIMER_ONCE){
                       manager
->remove_timer_(item);
                      item
->m_state=CTimer::TIMER_TIMEOUT;
                }

            
else if(item->m_type==CTimer::TIMER_CIRCLE){
/*循环性的要保证链表的顺序性,如要重新插入,保存entry_的原因,是执行新的插入后不要影响当前进行的循环*/
                    tmpTimer.entry_
=item->entry_;
                    manager
->remove_timer_(item);
                    manager
->add_timer_(item);
                   item
=&tmpTimer;}
                 }

         
else break;
        }

        pthread_mutex_unlock(
&manager->m_mutex);
    }

六、源码
写了个简单的makefile,执行make就可以生成测试程序:test,执行./test可以看运行效果。make由make so和make test组成,make so在lib目录下生成libtimer.a。make clean清空。
源代码下载这里http://www.cppblog.com/Files/CppExplore/timer.tar.gz  [make so有问题,貌似so是关键字,下载后把so改下,随意什么单词。]
七、后记
与上一版本相比,该文中的定时器实现要求在定时器模块运行期间不能修改系统实际,上一版本实现则无此限制。
定时器模块的锁初始化为fastmutex方式,因此在回调函数里注意不能再调用CTimer的start stop reset等方法,以免死锁,如果有调用的需求,可以把锁修改为循环锁recmutex方式。
 
2008/4/8补记:本文是timer的v2实现,定时器timer在业务线程中执行start的时候,要执行扫描排序操作,导致返回时间延长。后续修改:
(1)定时器timer的start操作不再执行扫描操作,而是简单插入队列头同时置一变量表示尚未排序。定时器线程延迟时间到,首先扫描未排序对象,执行排序(一次性timer从尾部扫描对比,循环性从头部扫描对比),再扫描判断是否超时。
(2)func移动到锁外执行。
针对windows客户端栈使用定时器不多,并且定时间隔不能受系统时间影响(客户端的系统时间可能被修改),仍然使用v1的实现。
posted on 2008-04-03 21:49 cppexplore 阅读(5083) 评论(10)  编辑 收藏 引用

评论

# re: 【原创】技术系列之 定时器(二) 2008-09-16 13:41 zam
很好,能否发一个V3版本的源码给我,非常感谢!
我的E-mail: zam1208@sohu.com  回复  更多评论
  

# re: 【原创】技术系列之 定时器(二) 2008-10-13 16:11 薛军
我也想要一份V3版本的源码,谢谢!xj181818◎163.com
  回复  更多评论
  

# re: 【原创】技术系列之 定时器(二) 2008-11-20 14:59 wangwb
请发跟我一份吧,学习一下,非常感谢!telventbbs@126.com  回复  更多评论
  

# re: 【原创】技术系列之 定时器(二) 2008-12-08 21:51 liujingnan
我也想要一份V3版,多谢了。liujingnan829@163.com  回复  更多评论
  

# re: 【原创】技术系列之 定时器(二) 2009-02-06 17:06 nihao
大侠,能不能也给我发一份v3版本的源码呀。xujie@m165.com
多谢!!!!!!!!!!!!  回复  更多评论
  

# re: 【原创】技术系列之 定时器(二)[未登录] 2009-02-19 10:38 cppexplore
楼上的各位啊 欢迎来讨论理论或者思想 具体到代码细节的东西就免了,文章已经很详细很详细了。  回复  更多评论
  

# re: 【原创】技术系列之 定时器(二) 2009-09-28 18:04 neou
可以去看看linux的定时器实现  回复  更多评论
  

# re: 【原创】技术系列之 定时器(二)[未登录] 2009-09-29 08:30 cppexplore
@neou
看过。
两码事情,8253芯片计时,os里中断处理。  回复  更多评论
  

# re: 【原创】技术系列之 定时器(二)[未登录] 2010-01-31 22:26 expter
linux定时器 是基于时间轮的,特别对于高性能服务器效率问题应该值得关注!

你的插入add_timer 还是单链表的排序插入,效率不是很高!
:)

  回复  更多评论
  

# re: 【原创】技术系列之 定时器(二) 2010-02-01 11:36 cppexplore
@expter
的确不高,呵呵
现在换成线程独自的定时器了,不再加锁,容器换简单的multimap了  回复  更多评论
  


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理