数组实现定时器

简述

  定时器是游戏服务器必备的一个功能组件,文章介绍如何用数组结构实现一个定时器。

数据结构

  每个定时器主要包含以下信息:

typedef struct TimerInfo
{
    int id;
    long expired;     //超时时间戳(ms)
    bool is_working;  //用于判断该定时器是否有效
    bool is_repeat;  // 是否为循环定时器标识
    long interval; //循环定时器循环间隔

} TimerInfo;

id是全局唯一,后面可用于取消未生效的定时器。

除了以上信息,还应包含定时器要执行的任务(一般是函数闭包),这里为简化代码未写出.

 

  定时器管理器:

typedef struct TimerMng
{
    int id;
    vector<shared_ptr<TimerInfo> > timer_array;   //已序数组
    map<int, shared_ptr<TimerInfo> > timer_map;   //id到定时器的映射 用于根据id取消定时器

    mutex mtx;

    struct timeval now;

    TimerMng()
    {
        id = 0;
    }

} TimerMng;

timer_array是已序数组,根据定时器超时时间排序。

timer_map是id到定时器的映射,用于取消未生效的定时器。

 

实现代码:

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


几个关键点介绍:

1.  定时器线程执行间隔是1ms,所以加入的定时器的最小循环间隔是1ms。

2.  add_timer 和 execute_timer对timer_mng的 timer_array 和 timer_map操作前,必须先加锁.

3.  add_timer是使用二分法(lower_bound)去寻找合适的位置插入新的定时器.

 

各个操作效率:

1. add_timer, 使用二分法寻找 O(log(N)); 数组插入 O(N);  红黑树插入 O(log(N))    ==>   O(N) + 2 * O(log(N)).

2. execute_timer,  数组删除 O(N); 红黑树删除 O(log(N))        ==>       O(N) + O(log(N)).

3. cancle_timer(未列出), 红黑树寻找 O(log(N))    ==>    O(log(N)).

 

add_timer时数组元素的插入会引起后面元素的移动, execute_timer必定会删除第一个元素,引起后面所有元素的移动,

这两个是非常耗时的操作,也是数组实现定时器的一大缺点,后面会介绍使用更高效的数据结构去实现.

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