优先队列实现定时器

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

posted @ 2017-02-01 11:19  逸马闪骑  阅读(860)  评论(0编辑  收藏  举报