系統梳理主流定時器演算法實現的差異以及應用
這一篇文章系統的梳理主流定時器演算法實現的差異以及應用地方。
1. 定時器介紹
程式裡的定時器主要實現的功能是在未來的某個時間點執行相應的邏輯。在定時器模型中,一般有如下幾個定義。
interval:間隔時間,即定時器需要在interval時間後執行
StartTimer:新增一個定時器任務
StopTimer:結束一個定時器任務
PerTickBookkeeping: 檢查定時器系統中,是否有定時器例項已經到期,相當於定義了最小時間粒度。
常見的實現方法有如下幾種:
連結串列
排序連結串列
最小堆
時間輪
接下來我們一起看下這些方法的具體實現原理。
2. 定時器實現方法
2.1 連結串列實現
連結串列的實現方法比較粗糙。連結串列用於儲存所有的定時器,每個定時器都含有interval 和 elapse 兩個時間引數,elapse表示當前被tickTimer了多少次。當elapse 和interval相等時,表示定時器到期。
在此方案中,新增定時器就是在連結串列的末尾新增一個節點,時間複雜度是 O(1)。如果想要刪除一個定時器的話,我們需要遍歷連結串列找到對應的定時器,時間複雜度是O(n)。此方案下,每隔elapse時間,系統呼叫訊號進行超時檢查,即PerTickBookkeeping。每次PerTickBookkeeping需要對連結串列所有定時器進行 elapse++,因此可以看出PerTickBookkeeping的時間複雜度是O(N)。可以看出此方案過於粗暴,所以使用場景極少
2.2 排序雙向連結串列實現
排序雙向連結串列是在連結串列實現上的最佳化。最佳化思路是降低時間複雜度。
首先,每次PerTickBookkeeping需要自增所有定時器的elapse變數,如果我們將interval變為絕對時間,那麼我們只需要比較當前時間和interval時間是否相等,減少了對每個定時器的操作。如果不需要對每個定時器進行操作,我們將定時器進行排序,那麼每次PerTickBookkeeping都只需要判斷第一個定時器,時間複雜度為O(1)。相應的,為了維持連結串列順序,每次新增定時器需要進行連結串列排序時間複雜度為 O(N)。每次刪除定時器時,由於會持有自己節點的引用,所以不需要查詢其在連結串列中所在的位置,所以時間複雜度為O(1),雙向連結串列的好處。
圖1 雙向連結串列實現示意圖
2.3 時間輪實現
時間輪示意圖如下:
圖2 時間輪
時間輪的資料結構是陣列 + 連結串列。 他的時間輪為陣列,新增和刪除一個任務,時間複雜度都是O(1)。PerTickBookkeeping每次轉動一格,時間複雜度也是O(1)。
2.4 最小堆實現
最小堆是堆的一種, (堆是一種二叉樹), 指的是堆中任何一個父節點都小於子節點, 子節點順序不作要求。
二叉排序樹(BST)指的是: 左子樹節點小於父節點, 右子樹節點大於父節點, 對所有節點適用
圖3 最小堆
樹的基本操作是插入節點和刪除節點。對最小堆而言,為了將一個元素X插入最小堆,我們可以在樹的下一個空閒位置建立一個空穴。如果X可以放在空穴中而不被破壞堆的序,則插入完成。否則就執行上濾操作,即交換空穴和它的父節點上的元素。不斷執行上述過程,直到X可以被放入空穴,則插入操作完成。因此我們可以知道最小堆的插入時間複雜度是O(lgN)。最小堆的刪除和插入邏輯基本類似,如果不做最佳化,時間複雜度也是O(lgN),但是實際實現方案上,做了延遲刪除操作,時間複雜度為O(1)。
延遲刪除即設定定時器的執行回撥函式為空,每次最小堆超時,將觸發pop_heap,pop會重新調整最小堆,最終刪除的定時器將調整到堆頂,但是回撥函式不處理。
可以看到PerTickBookkeeping只處理堆頂定時器,時間複雜度O(1)。最小堆可以使用陣列來進行表示,陣列中,當前下標n的左子節點為2N + 1,當前下標n的右子節點小標為2N + 2。
圖4 最小堆的陣列表示
3. 定時器不同實現對比
3.1 時間複雜度對比
圖5 不同實現時間複雜度
從上面的介紹來看,時間輪的時間複雜度最小、效能最好。
3.2 使用場景來看
在任務量小的場景下:最小堆實現,可以根據堆頂設定超時時間,陣列儲存結構,節省記憶體消耗,使用最小堆可以得到比較好的效果。而時間輪定時器,由於需要維護一個執行緒用來撥動指標,且需要開闢一個bucket陣列,消耗記憶體大,使用時間輪會較為浪費資源。在任務量大的場景下:最小堆的插入複雜度是O(lgN), 相比時間輪O(1) 會造成效能下降。更適合使用時間輪實現。在業界,服務治理的心跳檢測等功能需要維護大量的連結心跳,因此時間輪是首選。
更多免費技術資料及影片
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69976011/viewspace-2696994/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 系統之間差異
- 定時器以及定時器的幾個案例定時器
- Yolov8和Yolov10的差異以及後處理實現YOLOv1
- Linux時間子系統之七:定時器的應用--msleep(),hrtimer_nanosleep()Linux定時器NaN
- 並查集演算法Union-Find的思想、實現以及應用並查集演算法
- 2.系統定時器定時器
- Python 基於 selenium 實現不同商城的商品價格差異分析系統Python
- Android應用商店差異化思路Android
- Linux時間子系統之七:定時器的應用--msleep(),hrtimer_nanosleep()【轉】Linux定時器NaN
- 加推時序系統RTS實現原理及應用簡介
- 如何用 Java 實現 Web 應用中的定時任務?JavaWeb
- Kafka+Flink 實現準實時異常檢測系統Kafka
- 北斗授時伺服器(時鐘系統)在政務系統應用方案伺服器
- 國內外的ERP系統存在顯著的差異,差在哪?
- 準實時異常檢測系統
- 【JavaScript定時器小案例】常見的幾種定時器實現的案例JavaScript定時器
- android應用實現重啟系統Android
- 應用設定Setting的實現
- Java Web應用下實現定時任務的簡便方法JavaWeb
- 閉包,定時器以及BOM定時器
- Go定時器的三種實現方式Go定時器
- 直播系統原始碼,實現倒數計時,定時任務原始碼
- 談談MES系統差異產生的主要方式
- 老外看中國:本土移動應用差異在哪?
- [奇思異想]使用RabbitMQ實現定時任務MQ
- Section 7_SysTick系統定時器定時器
- dbms_job包的應用:在Oracle中實現定時操作(轉)Oracle
- javascript基礎(定時器的應用)(四十三)JavaScript定時器
- Linux應用層的定時器TimerLinux定時器
- 應用黑名單以及靜默安裝的實現
- C++定時器CTimer的實現C++定時器
- Spring定時器的兩種實現方式Spring定時器
- 現實生活:定時器&孔子的一生定時器
- UML 在系統設計時的應用
- Thunk程式的實現原理以及在iOS中的應用iOS
- 安卓應用修改系統時間安卓
- OpenGL應用——實現DDA演算法演算法
- Linux系統中Ubuntu和Redhat的差異有哪些?LinuxUbuntuRedhat