time_heap(時間堆)
- 適合事件驅動系統的時間堆定時器,所有接口都儘量設計的簡單易用。
- 現在大部分流行的定時器一般分爲兩種,時間輪,時間堆。基於設計上而言。時間輪可以設計爲滴答檢測超時事件,或者直接在循環中檢查超時事件。而時間堆異同。
- 我之前就寫過一個基於滴答檢測超時事件的定時器,這樣的機制會造成一個問題。那就是由於休眠喚醒的時間總會有微小的誤差(每次滴答),那麼每次的誤差積累起來就會越來越大。導致定時時長過長的事件的時間誤差越大。所以只能在某些不需要太過精準定時時長的地方使用。
- 接下來我們來看看在循環裏檢測超時事件的定時器吧,這種類型的定時器只有一個問題。那就是佔用不必要的性能消耗。如果是在事件驅動系統那麼將造成很大的性能浪費。
- 那我們的時間堆呢。它有問題嗎?當然有。那就是如果連續插入大量定時時長過短的定時事件那麼就會造成事件觸發事件延遲,延遲的時間根據事件的數量和觸發時間而定。
- 好處:
- 不浪費性能,使用WaitForSingleObject函數直接休眠到下一個事件觸發時間再喚醒執行任務;
- 適合核心數較少的cpu, 不會出現一個循環佔滿cpu的情況;
性能測試(Release_x64)
- 10w_50ms_timer, 從添加定時任務到最後一個定時任務結束消耗230ms左右, 循環檢測類定時器消耗30ms左右
- 100w_50ms_timer, 從添加定時任務到最後一個定時任務結束消耗2300ms左右, 循環檢測類定時器消耗350ms左右
- 10w_2000ms_timer, 從添加定時任務到最後一個定時任務結束消耗150ms左右, 循環檢測類定時器消耗30ms左右
- 100w_2000ms_timer, 從添加定時任務到最後一個定時任務結束消耗1200ms左右, 循環檢測類定時器消耗280ms左右
上面已經說了優缺點,接下來說下可以優化的點:
- 在時間堆中使用內存池。
- 加入異步執行任務。
備註:如果不在意性能消耗可以使用循環類檢測超時任務的定時器,更加精準.
使用代碼:
auto& timer = time_heap::instance();
double second = 5; //定時時長(單位:秒,由於是double可以傳入0.05這樣的數字,會自動轉爲毫秒)
uint32_t count = 5; //執行次數
//timer.add函數的返回值是一個std::shared_ptr對象。不用手動銷燬
auto timer1 = timer.add(second, count, [&](int index) {
printf("%d\n");
return index;
}
, 1); //不定參,可以使用任意類型個數的參數
//獲取事件函數返回值(若未事件未執行完成將堵塞)
auto result = timer1->get();
//安全的獲取事件函數返回值(若未事件未執行完成將堵塞,超過count次get完成後再次get將cash)
if (timer1->vaild) {
auto result = timer1->get();
}
timer1->del(); //刪除定時事件
代碼庫在github–> 點擊此處進入