定時器事件簡介

簡單點說,定時事件是到了一個時間點就需要提示去執行某個動作。首先想到的利用信號,一般有兩組設置定時信號的函數:alarm和setitimer。

alarm是設置一次實時的延遲,單位是秒,信號是SIGALRM。相關man手冊

setitimer用來設置一個間隔時間,週期性的觸發信號。系統給進程提供了不同的時間,實時時間、進程運行時間、進程運行和系統支持時間。設置時候的選項和觸發信號也不一樣。實時時間設置選項和觸發信號分別是ITIMER_REAL和SIGALRM。相關man手冊

定時事件是服務器中需要經常處理的事件之一,比如處理非活動鏈接。容納定時時間的稱爲定時器容器,簡稱定時器。一般處理定時器的數據結構有三種:升序定時器鏈、時間輪、時間堆。

升序定時器鏈是用鏈表組織的結構,保證每次插入之後整個定時器鏈仍然有序。
這裏寫圖片描述
每次處理定時事件從鏈表頭開始處理,直到第一個比定時時間更大的定時器出現爲止。從執行效率上來看,添加定時器的時間複雜度是O(n),刪除定時器的時間複雜度是O(1),執行一次處理定時事件的時間複雜度是O(1)

PS:《Linux高性能服務器編程》中認爲處理定時事件的時間複雜度是O(1),我持有疑問。因爲執行一次處理有可能遍歷整個事件鏈,即使時間分佈均勻分散,每次處理也是鏈長度的幾分之一,與n有關。

時間輪是一種分層模型,應用了哈希的思想。先直觀上來看:
這裏寫圖片描述
每一個定時器都被散列到一個輪槽上面。添加定時器時如何計算槽號:
散列的槽號=當前槽號+(定時度過時長%一圈時長)/一槽的時長
每一個槽對應的是一個定時器鏈,執行定時事件就是遍歷當前槽號對應的定時器鏈上的定時器,看是否超過定時時間。每一次執行定時事件之後就看當前累積時間是否超過一槽的時長,如果超過就需要將指針移動到下一個槽號。時間輪的效率很高,添加一個定時器的時間複雜度是O(1),刪除一個定時器的時間複雜度也是O(1),執行一次定時事件的時間複雜度是O(n)。但因爲所有定時器被散列到不同的槽上面,所以實際上的執行效率比O(n)要好得多。
時間輪也非常易於擴展,當有多個精度或者時間跨度很大的時候,能夠有多個不同粒度的時間輪。相鄰兩個時間輪,高精度的轉動一圈,低精度的移動一槽,像老式的機械水錶一樣。

時間堆是另外一種設計思路:將所有定是其中超時時間最小的作爲定時間隔。每次執行定時事件處理都會處理到那個超時時間最小的定時器,同時選出剩下的定時器中選出超時時間最小的,設置這個時間爲下一次的定時時間。因爲需要經常執行尋找最小,所以用最小堆來管理所有的定時器。
這裏寫圖片描述
時間堆可以實現較爲精確的定時,添加定時器的時間複雜度是O(logn),刪除和執行定時器的時間複雜度是O(1)。

參考資料
《Linux高性能服務器編程》
http://www.cnblogs.com/zhongwencool/p/timing_wheel.html
http://www.ibm.com/developerworks/cn/linux/l-cn-timers/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章