时间轮
老早之前就聽說時(shí)間輪算法特別高效,Linux內(nèi)核都用的它,這兩天抽空實(shí)現(xiàn)了遍……嗯,被差一bug搞死(~ ̄▽ ̄~) 啊哈
網(wǎng)上扣來的圖,原理好懂:輪子里的每格代表一小段時(shí)間(精度),連起來就能表示時(shí)間點(diǎn)了(我去年買了個(gè)表),格子內(nèi)含鏈表,中存回調(diào)函數(shù);時(shí)間指針每次轉(zhuǎn)動(dòng)一格,指向某格時(shí),取出鏈表里的回調(diào)函數(shù)依次執(zhí)行,后清空鏈表,等待下一次轉(zhuǎn)動(dòng)。
加入節(jié)點(diǎn)邏輯也簡單:在輪子可表示的時(shí)間范圍內(nèi)(格子數(shù)*格子精度),配合當(dāng)前時(shí)針位置,對(duì)格子總數(shù)取余,即得本節(jié)點(diǎn)需放哪個(gè)格子。
進(jìn)一步為擴(kuò)大時(shí)間輪的表示范圍,使用分級(jí)方式,跟時(shí)分秒一樣,上一級(jí)轉(zhuǎn)一圈,下一級(jí)動(dòng)一格。
?
對(duì)吧,很容易理解……然后coding是另一碼事(╯‵□′)╯︵┴─┴
首先,最重要的,數(shù)據(jù)結(jié)構(gòu):用了環(huán)形鏈表,方便增刪。
struct NodeLink {NodeLink* prev;NodeLink* next;NodeLink() { prev = next = this; } //circle }; struct stWheel {NodeLink* slots; //每個(gè)slot維護(hù)的node鏈表為一個(gè)環(huán),slot->next為node鏈表中第一個(gè)節(jié)點(diǎn),prev為node的最后一個(gè)節(jié)點(diǎn)const uint32 size;uint32 slotIdx;stWheel(uint32 n) : size(n), slotIdx(0){ slots = new NodeLink[size]; }~stWheel() {if (slots) {for (uint32 j = 0; j < size; ++j) {NodeLink* link = (slots + j)->next;while (link != slots + j) {TimerNode* node = (TimerNode*)link;link = node->link.next;delete node;}}delete[]slots;}} };具體時(shí)間節(jié)點(diǎn)的數(shù)據(jù)結(jié)構(gòu)如下:
struct TimerNode {Pool_Obj_Define(TimerNode, 32) //內(nèi)存池聲明,不含數(shù)據(jù)NodeLink link; //must in the head uint32 timeDead;uint32 interval; //間隔多久int loop; //總共循環(huán)多久std::function<void()> func; };TimeNode里保存上下關(guān)系,stWheel的NodeLink輔助用的,環(huán)狀鏈表的頭,沒實(shí)際數(shù)據(jù),用以記錄首尾TimeNode。
核心代碼如下:
——增刪節(jié)點(diǎn)——
void CTimerMgr::_AddTimerNode(uint32 milseconds, TimerNode* node) {NodeLink* slot = NULL;uint32 tickCnt = milseconds / TIME_TICK_LEN;if (tickCnt < WHEEL_CAP[0]) {uint32 index = (_wheels[0]->slotIdx + tickCnt) & (WHEEL_SIZE[0] - 1); //2的N次冪位操作取余slot = _wheels[0]->slots + index;} else {for (int i = 1; i < WHEEL_NUM; ++i) {if (tickCnt < WHEEL_CAP[i]) {uint32 preCap = WHEEL_CAP[i - 1]; //上一級(jí)總?cè)萘考礊楸炯?jí)的一格容量uint32 index = (_wheels[i]->slotIdx + tickCnt / preCap - 1) & (WHEEL_SIZE[i] - 1); //勿忘-1slot = _wheels[i]->slots + index;break;}}}NodeLink* link = &(node->link);link->prev = slot->prev; //插入格子的prev位置(尾節(jié)點(diǎn))link->prev->next = link;link->next = slot;slot->prev = link; } void CTimerMgr::RemoveTimer(TimerNode* node) {LOG_TRACK("node[%p], timeDead[%lld]", node, node->timeDead);NodeLink* link = &(node->link);if (link->prev) {link->prev->next = link->next;}if (link->next) {link->next->prev = link->prev;}link->prev = link->next = NULL;delete node; }——輪子啟動(dòng)——
void CTimerMgr::CheckTimerList(const uint32 timenow) {uint32 tickCnt = timenow > _checkTime ? (timenow - _checkTime) / TIME_TICK_LEN : 0;//if (tickCnt) Printf();for (uint32 i = 0; i < tickCnt; ++i) { //掃過的slot均超時(shí)stWheel* wheel = _wheels[0];NodeLink* slot = wheel->slots + wheel->slotIdx;NodeLink* link = slot->next;slot->next = slot->prev = slot; //清空當(dāng)前格子while (link != slot) { //環(huán)形鏈表遍歷TimerNode* node = (TimerNode*)link;link = node->link.next; //得放在前面,后續(xù)函數(shù)調(diào)用,可能會(huì)更改node的鏈接關(guān)系 AddToReadyNode(node);}if (++(wheel->slotIdx) >= wheel->size) {wheel->slotIdx = 0;Cascade(1, timenow); //跳級(jí) }_checkTime += TIME_TICK_LEN;}DoTimeOutCallBack(); } uint32 CTimerMgr::Cascade(uint32 wheelIdx, const uint32 timenow) {if (wheelIdx < 1 || wheelIdx >= WHEEL_NUM) {return 0;}int casCnt = 0;stWheel* wheel = _wheels[wheelIdx];NodeLink* slot = wheel->slots + wheel->slotIdx;NodeLink* link = slot->next;slot->next = slot->prev = slot; //清空當(dāng)前格子while (link != slot) {TimerNode* node = (TimerNode*)link;link = node->link.next;if (node->timeDead <= timenow) {AddToReadyNode(node);} else {_AddTimerNode(node->timeDead - timenow, node); //本級(jí)精度下已超時(shí),精度提升,重新加一遍++casCnt;LOG_TRACK("wheelIdx[%u], link[%p], milseconds[%u]", wheelIdx, link, node->timeDead - timenow);}}if (++(wheel->slotIdx) >= wheel->size) {wheel->slotIdx = 0;casCnt += Cascade(++wheelIdx, timenow);}return casCnt; }?
那么問題來了:大于,大于等于,邊界,減一……搞錯(cuò)幾多次 ○(* ̄︶ ̄*)○ 吃飽睡好
?
?
源碼地址:https://github.com/3workman/Tools/tree/master/src/Timer
轉(zhuǎn)載于:https://www.cnblogs.com/3workman/p/6063819.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
- 上一篇: ffmpeg 纯静态编译,以及添加自定义
- 下一篇: concurrent包下的Exchang