软件定时器设计

定时器的作用

定时器的主要用途是执行定时任务,就比如闹钟,定一个每天早上7点的闹钟,闹钟响了之后要执行起床刷牙的动作,地球也在执行定时操作,每24小时自转一圈,最近有新闻说地球自转越来越快了,地球定时器也不太准啊(lll¬ω¬)。上面举的两个例子就是循环定时器,只要你不取消闹钟,地球不爆炸,定时器就会一直存在,还有一些一次性定时任务,执行完就结束了,比如你定了一个抢火车票的任务,抢到票了任务就已完成。

软件定时器

软件世界的定时器与现实世界的定时器类似,也有循环定时任务和一次性定时任务,根据使用场景不同方案有所区别。

选取定时函数

在现实世界中如果没有手表,我们可以根据太阳的东升西落知道一天过去了,“一天”这个时间精度是粗糙的。有了手表之后,想执行一个定时任务,你可以不停的看表(这就叫轮询),比如10点钟我要抢火车票,从9点50分我就隔一会儿看一次手表,直到10点执行抢票任务。有了闹钟(手机)之后,我定一个10点钟定时器,这样我就可以做别的事情,到了10点闹钟一响,我立马停止手上的事情,执行抢票任务,这种就叫中断。

什么是定时函数呢h4>

定时函数就是闹钟,可以提醒程序到了执行任务的时间。如下图所示,常用的定时函数:

软件定时器设计
前四个sleep函数,会让调用线程挂起,原地等待定时器超时,挂起可以理解为等待的这段时间啥都干不了,就类似于你站在起跑线上,裁判10 9 8 7 6 5 4 3 2 1倒数,你知道10s后才起跑呢,你能站起来扭扭胯胯轴吗,不行,你得原地等待。

alarm()和setitimer(),它们的通知机制采用了信 SIGALRM,由于SIGALRM信 不可靠,会造成超时通知不可靠,而且多线程中处理信 也是一个麻烦事,也不考虑。

timer_create()/timer_settime()系列函数是POSIX规定,精度达到纳秒级,提供了一个数据结构struct sigevent可以指定一个实时信 作为通知信 ,同时也可以设置线程ID,将信 传递到指定的线程。相比前两个函数,有了不小的改进,可以作为一个备选的实现,但是可以预见到封装起来不会很轻松。此外使用此系列的函数,需要链接librt库。

事实上,我们遗漏掉了几个同样具有定时的功能的API——多路复用。在Linux上的多路复用机制有select/poll/epoll几种,它们轮询时都允许指定一个超时时间,如果在指定时间内,监控的事件没有到达,轮询函数会超时返回。精度也足够用,poll/epoll是毫秒级的(millisecond),select超时参数是struct timeval,是微秒级的(microsecond)。

选择epoll的优势很明显,能将定时功能完美的融入已有的event loop里,同时epoll有着天然的高并发的能力,millisecond级的精度也足够用。
参考文章:
https://cloud.tencent.com/developer/article/1763594

定时函数如何选取

根据你的使用场景,举两个例子:
1.多线程
使用多路复用机制select/poll/epoll都要求有一个线程专门执行多路任务的判断,这个线程在没有任务的时候会休眠以节省系统开销。
多路复用的优点是定时线程只管保证定时提醒,超时任务又其他线程执行,二者不会相互影响;由于定时线程和定时任务是异步的,同时要考虑线程间交互以及时间补偿。
2.单线程
如果你的软件系统没有多余的资源用于多路复用线程,则不能使用多路复用机制,则要在本线程中执行定时任务,这种机制类似于轮询,每一个时间间隔就查询是否有定时器超时,如果有则执行所有超时任务,如果超时函数执行时间过长,会印象定时器的准确性。

定时器管理

在软件应用中,根据定时长短不同,是否循环,回调函数不同,会需要创建很多定时器,如果管理这些定时器,使得创建,删除,执行定时器的效率最高,需要选择一种算法来管理。
这里可以参考文章
https://jishuin.proginn.com/p/763bfbd54473

声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2022年1月7日
下一篇 2022年1月7日

相关推荐