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