优先队列实现定时器

http://www.cnblogs.com/lewiskyo/p/6359789.html 上文介绍了使用数组实现定时器,但因为插入和删除定时器的效率太低,所以这里改用优先队列实现一次。

实现代码如下:

  1 #include <iostream>
  2 #include <sys/time.h>
  3 #include <unistd.h>
  4 #include <thread>
  5 #include <vector>
  6 #include <map>
  7 #include <memory>
  8 #include <mutex>
  9 #include <stdlib.h>
 10 #include <algorithm>
 11 #include <queue>
 12
 13 using namespace std;
 14
 15 #define THREAD_COUNT 4
 16
 17 typedef struct TimerInfo
 18 {
 19     int id;
 20     long expired;     //超时时间戳(ms)
 21     bool is_working;  //用于判断该定时器是否有效
 22     bool is_repeat;  // 是否为循环定时器标识
 23     long interval; //循环定时器循环间隔
 24
 25 } TimerInfo;
 26
 27 // 排序函数
 28 class comp
 29 {
 30 public:
 31     bool operator()(const shared_ptr<TimerInfo> &t1, const shared_ptr<TimerInfo> &t2)
 32     {
 33         if(t1.get()->expired > t2.get()->expired)
 34             return true;
 35         else
 36             return false;
 37     }
 38 };
 39
 40
 41 typedef struct TimerMng
 42 {
 43     int id;
 44
 45     priority_queue<shared_ptr<TimerInfo>, vector<shared_ptr<TimerInfo> >, comp> timer_queue;   // 优先队列
 46     map<int, shared_ptr<TimerInfo> > timer_map;   //id到定时器的映射 用于根据id取消定时器
 47
 48     mutex mtx;
 49
 50     struct timeval now;
 51
 52     TimerMng()
 53     {
 54         id = 0;
 55     }
 56
 57 } TimerMng;
 58
 59 TimerMng t_mng;
 60
 61 void add_timer(bool is_repeat, long expired, long interval)
 62 {
 63     TimerInfo *t_info = new TimerInfo();
 64     t_info->is_repeat = is_repeat;
 65     t_info->expired = expired;
 66     t_info->interval = interval;
 67     t_info->is_working = true;
 68
 69     shared_ptr<TimerInfo> timer_info(t_info);
 70
 71     lock_guard<mutex> guard(t_mng.mtx);
 72     int now_id = t_mng.id++;
 73     t_info->id = now_id;
 74
 75     t_mng.timer_queue.push(timer_info);
 76
 77     t_mng.timer_map.insert(pair<int, shared_ptr<TimerInfo> >(now_id, timer_info) );
 78
 79 }
 80
 81 void update_time()
 82 {
 83     gettimeofday(&t_mng.now, NULL);
 84 }
 85
 86 void execute_timer()
 87 {
 88     long now_usec = t_mng.now.tv_sec * 1000 + t_mng.now.tv_usec / 1000;
 89
 90     while(true)
 91     {
 92         lock_guard<mutex> guard(t_mng.mtx);
 93
 94         if (t_mng.timer_queue.empty())
 95             break;
 96
 97         shared_ptr<TimerInfo> first = t_mng.timer_queue.top();
 98
 99         if (first.get()->expired > now_usec )
100             break;
101
102         if (first.get()->is_working)
103         {
104             // do something here
105             cout << "timer execute succ, now: " << now_usec <<  " id: " << first.get()->id << " " << "expired: " << first.get()->expired << endl;
106         }
107
108         map<int, shared_ptr<TimerInfo> >::iterator map_iter = t_mng.timer_map.find( first.get()->id);
109
110         // 从map中移除
111         t_mng.timer_map.erase(map_iter);
112
113         // 从队列中移除
114         t_mng.timer_queue.pop();
115
116     }
117 }
118
119
120 void timer_thread()
121 {
122     while(1)
123     {
124         update_time();
125         execute_timer();
126         // 1ms 1次循环
127         usleep(1000);
128     }
129 }
130
131 void worker_thread()
132 {
133     srand((unsigned)time(0));
134     while(1)
135     {
136         struct timeval now;
137         gettimeofday(&now, NULL);
138
139         int rand_sec = rand() % 5 + 1;
140         int rand_usec = rand() % 900000 + 1;
141
142         long expired = (now.tv_sec + rand_sec) * 1000 + ( rand_usec / 1000 );
143         add_timer(false, expired, 0);
144
145         sleep(rand() % 2 + 1);
146     }
147 }
148
149
150 int main()
151 {
152     thread t1(timer_thread);
153
154     vector<thread> v_thread;
155
156     for (int i = 0; i < THREAD_COUNT; ++i)
157     {
158         v_thread.push_back(thread(worker_thread));
159     }
160
161     t1.join();
162
163     for (int i = 0; i < THREAD_COUNT; ++i)
164     {
165         v_thread[i].join();
166     }
167 }

代码与使用数组实现的大致相同,只是换了队列实现,而且在add_timer时直接使用优先队列的接口 push,

在移除定时器时使用接口 pop即可.

实现效率:

add_timer 已序堆中添加数据 O(log(N)).

execute_timer 最小已序堆中删除根节点,选取新的根节点 O(log(N)).

可见比起数组实现的效率要高.

优先队列(堆)的性质可以参考: http://blog.csdn.net/zhang20072844/article/details/10286997

时间: 2024-10-24 09:32:31

优先队列实现定时器的相关文章

storm定时器timer源码分析-timer.clj

storm定时器与java.util.Timer定时器比较相似.java.util.Timer定时器实际上是个线程,定时调度所拥有的TimerTasks:storm定时器也有一个线程负责调度所拥有的"定时任务".storm定时器的"定时任务"是一个vector类型的数据[time, callback, uuid],内有会有三个值,分别是时间.函数.和uuid,很好理解,时间表示该定时任务什么时候执行,函数表示要执行的函数,uuid用于标识该"定时任务&qu

Android零基础入门第60节:日历视图CalendarView和定时器Chronometer

原文:Android零基础入门第60节:日历视图CalendarView和定时器Chronometer 上一期学习了AnalogClock.DigitalClock和TextClock时钟组件,本期继续来学习日历视图CalendarView和定时器Chronometer. 一.CalendarView 日历视图(CalendarView)可用于显示和选择日期,用户既可选择一个日期,也可通过触 摸来滚动日历.如果希望监控该组件的日期改变,则可调用CalendarView的 setOnDateCha

发送短信验证码按钮 定时器

static int i = 29; @property(nonatomic,strong) NSTimer *timmer;//定时器 - (void)sendNumber{ NSLog(@"发送验证码"); self.getTelephoneCodeBtn.enabled = NO; [self.getTelephoneCodeBtn setTitle:@"已发送" forState:UIControlStateDisabled]; [self.getTelep

《TCP/IP具体解释》读书笔记(22章)-TCP的坚持定时器

TCP通过让接收方指明希望从发送方接收的数据字节数(即窗体大小)来进行流量控制. 假设窗体大小为0会发生什么情况呢?这将有效阻止发送方传送数据,直到窗体变为非0为止. ACK的传输并不可靠,也就是说,TCP不正确ACK报文段进行确认,TCP仅仅确认那些包括有数据的ACK报文段. 1.坚持定时器 假设一个场景:假设一个确认丢失了,则两方就有可能由于等待对方而使连接终止,接收方等待接收数据(由于它已经向发送方通告了一个非0的窗体),而发送方在等待同意它继续发送数据的窗体更新.为防止这种死锁情况的发生

javascript-函数和定时器

1.函数自执行 JavaScript中不像C#中那样要求所有路径都有返回值,没有返回值就是undefined (function(){alert(1);}()); (function(){alert(1);})(); !function(){alert(1);}(); void function(){alert(2);}(); 2.可变参数 js函数无法像C#方法那样重载 下面的代码等于是对a函数重新定义 function a() { alert("无参函数"); } function

&lt;JavaScript&gt; 八. 定时器

1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title></title> 5 <script type="text/javascript"> 6 /* 7 定时器 周期性执行JS代码 8 */ 9 10 // --------------------- 方法 ----------------------- 11 /* 12 1. setInterval(code, millisec

51nod1428(优先队列)

题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1428 题意:中文题诶- 思路:贪心 问最少要多少教室就是求最多有多少个时间段产生了交集咯.我们先用结构体存储区间并将其按照左端点升序排列,若左端点相同则按右端点升序排列. 接下来遍历所有区间,并维护一个优先队列,其中存储区间右端点值.对于当前区间,我们将优先队列中所有比当前区间左端点小的元素删除(因为其所在区间不会与当前区间相交嘛),然后再将当前区间的右端点加

JavaScript定时器原理分析

.header { cursor: pointer } p { margin: 3px 6px } th { background: lightblue; width: 20% } table { text-align: center; margin-top: 20px; margin-left: 10px; margin-bottom: 20px } a { cursor: pointer; text-decoration: none; color: gray } a:hover { text

workman源代码阅读 - 使用信号处理器实现定时器

<?php /** * SIGALRM信号处理器注册成功后,在什么情况下进程会收到该信号呢? * * 在Linux系统下,每个进程都有惟一的一个定时器,该定时器提供了以秒为单位的定时功能.在定时器设置的超时时间到达后,调用alarm的进程将收到SIGALRM信号. */ /** * 启动信号处理器 */ \MySignalClazz::init(); /** * 信号处理器 * @author Administrator * */ class MySignalClazz { /** * Task