記錄——時間輪定時器(lua 實現)


http://www.cnblogs.com/mmc1206x/p/6849172.html

 很長一段時間里,我錯誤的認識了定時器。無意中,我發現了“時間輪”這個名詞,讓我對定時器有了新的看法。

 

  我錯誤的認為,定時器只需要一個 tick 隊列,按指定的時間周期遍歷隊列,檢查 tick 倒計時滿足觸發條件就觸發回調。

tick 定義如下:

1 struct Tick {
2     int_t n;
3     func_t func;
4 };

遍歷觸發實現如下:

復制代碼
 1 void Update()
 2 {
 3     for (auto & tick: _ticks)
 4     {
 5         if (Check(tick))
 6         {
 7             tick.func();
 8             Remove(tick);
 9         }
10     }
11 }
復制代碼

實現很簡潔,但效率卻出奇的慢。

假設有100個tick,依次觸發時間是100~10000毫秒,也就是每一個tick的觸發間隔為100毫秒

可以想象,在頭100毫秒內,不會有任何tick被觸發,但是Update卻傻乎乎對100個tick進行Check。

當時間達到100毫秒的時候,只有第一個 tick 達到了觸發條件,但是Update依舊會對余下99個進行Check。

 

時間輪很好的解決了這個問題。

思路是這樣的:

需要一個輪盤,輪盤上有若干個插槽,

把 tick 放進合適的插槽,

每次輪詢直接觸發插槽里的 tick。

 

假設要實現一個最低刻度為毫秒,最大上限為1天的定時器(最長延時23點59分59秒999毫秒)

假設要在 3點30分25秒600毫秒 處安插一個 tick。

首先這個輪盤需要 24 × 60 x 60 x 1000 個插槽,

其次把 3點30分25秒600毫秒 轉化為定時器最低刻度(毫秒),也就是 11005600 = 600 + 25000 + 180000 + 10800000.

也就是說,在這個輪盤的 11005600 刻度位置,安插上這個 tick。

就是這么簡單粗暴!

這是一個錯誤例子。

 

其實不需要那么多插槽,如果你見過水表,你應該知道該怎么做,繼續前面的假設。

我們為每一個時間單位准備一個時間輪,就是 時(24),分(60),秒(60),毫秒(1000)

因此只需要 1144 = 24 + 60 + 60 + 1000 個插槽就夠了。

在 3點30分25秒600毫秒 處安插一個 tick,

首先在 時(3) 安插上這個 tick,

當執行到 時(3) 的時候,刪除這個 tick,檢查到該 tick 還有 30分25秒600毫秒

於是在 分(30)安插上這個 tick,

當執行到 分(30)的時候,刪除這個 tick,檢查到該 tick 還有 25秒600毫秒

於是在 秒(25)安插上這個 tick,

當執行到 秒(25)的時候,刪除這個tick,檢查到該 tick 還有 600毫秒

於是在 毫秒(600)安插上這個 tick,

當執行到 毫秒(600)的時候,刪除這個tick,觸發這個 tick。

這個 tick 從被安插到被觸發,總共只需要 Check(4) 次。

如果采用本文開頭的思路,那將會被 Check(天文數字)次。

 

因為只是為了理解算法,我只是用lua實現了一遍,算法本身大概只有90行不到,吐個槽,lua索引從1開始很蛋疼。

復制代碼
 1 sformat = string.format
 2 tinsert = table.insert
 3 tremove = table.remove
 4 tconcat = table.concat
 5 mfloor = math.floor
 6 local utils = require("utils")
 7 local _M = {    _slots = nil, 
 8                 _cycle = nil,    }
 9 
10 function _M.Init(self, cycle)
11     if not self._slots then
12         self._slots = {}
13         self._slots[1] = {}
14         self._slots[2] = {}
15         self._slots[3] = {}
16         self._slots[4] = {}
17         utils.tinsert_n(self._slots[1], {}, 24)
18         utils.tinsert_n(self._slots[2], {}, 60)
19         utils.tinsert_n(self._slots[3], {}, 60)
20         utils.tinsert_n(self._slots[4], {}, 1000)
21     end
22     if not self._cycle then
23         self._cycle = cycle
24     end
25 end
26 
27 function _M.Update(self, cycle)
28     local h1, m1, s1, ms1 = utils.ms2t(self._cycle)
29     self._cycle = cycle
30     local h2, m2, s2, ms2 = utils.ms2t(self._cycle)
31     self:__UpdateT__(24, 1, h1, h2, utils.bind(self.__UpdateH__, self))
32     self:__UpdateT__(60, 2, m1, m2, utils.bind(self.__UpdateM__, self))
33     self:__UpdateT__(60, 3, s1, s2, utils.bind(self.__UpdateS__, self))
34     self:__UpdateT__(1000, 4, ms1, ms2, utils.bind(self.__UpdateMS__, self))
35 end
36 
37 function _M.AddTimer(self, delay, func)
38     self:__Insert__(delay + 1, func)
39 end
40 
41 function _M.__Insert__(self, delay, func)
42     if 0 == delay then
43         func()
44     else
45         local h1, m1, s1, ms1 = utils.ms2t(delay)
46         local h2, m2, s2, ms2 = utils.ms2t(delay + self._cycle)
47         local tick = {    func = func, 
48                         time = { h = h2, m = m2, s = s2, ms = ms2 } }
49         if h1 ~= 0 then
50             tinsert(self._slots[1][h2 == 0 and 24 or h2], tick)
51         elseif m1 ~= 0 then
52             tinsert(self._slots[2][m2 == 0 and 60 or m2], tick)
53         elseif s1 ~= 0 then
54             tinsert(self._slots[3][s2 == 0 and 60 or s2], tick)
55         elseif ms1 ~= 0 then
56             tinsert(self._slots[4][ms2 == 0 and 1000 or ms2], tick)
57         end
58     end
59 end
60 
61 function _M.__UpdateT__(self, cycle, index, first, last, func)
62     local slots = self._slots[index]
63     while first ~= last do
64         first = first + 1
65         for i = 1, #slots[first] do
66             func(slots[first][i])
67         end
68         slots[first] = {}
69         first = first % cycle
70     end
71 end
72 
73 function _M.__UpdateH__(self, v)
74     self:__Insert__(utils.t2ms(0, v.time.m, v.time.s, v.time.ms), v.func)
75 end
76 
77 function _M.__UpdateM__(self, v)
78     self:__Insert__(utils.t2ms(0, 0, v.time.s, v.time.ms), v.func)
79 end
80 
81 function _M.__UpdateS__(self, v)
82     self:__Insert__(utils.t2ms(0, 0, 0, v.time.ms), v.func)
83 end
84 
85 function _M.__UpdateMS__(self, v)
86     self:__Insert__(utils.t2ms(0, 0, 0, 0), v.func)
87 end
88 
89 return _M
復制代碼

 

源碼下載

 
 
分類: 雜項

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM