乙個基於優先佇列的C 定時器

2021-10-09 12:46:04 字數 4259 閱讀 9776

chrono庫類實現

定時器的實現方式有多種,底層實現有雙向鍊錶,最小堆實現以及時間輪。

本文以基於最小堆實現乙個簡單的定時器。使用了c++ 11中實現了priority_queue模板,雖然名字中帶有queue,但是實現方式是堆,名字中帶有queue,只是因為使用queue的介面。

鍊錶是一種簡單的資料結構。只需要一條公升序的鍊錶即可實現乙個簡單的定時器。每一次有新的定時器加入,則從煉表頭開始遍歷,直到遇到可插入的位置。

在時間複雜度上,使用鍊錶編寫的定時器插入和刪除的時間複雜度皆為o(n)。

最小堆在c++ 11 中提供了std::priority_queue模板。std::priority_queue預設構造為最大堆。如果需要使用最小堆,則需在構造時新增第三個引數,乙個仿函式。

例如:我需要利用std::priority_queue實現公升序timer。

using timepoint = std::chrono::high_resolution_clock::time_point;

timer

;private

: timepoint expiretime_;

}struct timercmp };

std::priority_queue , std::vector>

,timercmp> mangerqueue_;

在插入和刪除上,堆的時間複雜度皆為o(logn)

時間輪是一種稍微複雜點的資料結構。有簡單單層時間輪,也有分層時間輪。

單層時間輪較為簡單就不展開討論了。這裡說一說分層時間輪。分層時間輪每乙個粒度對應乙個時間輪,由多個時間輪進行度調。解決了單層時間輪刻度過大造成的空間浪費以及刻度過小時任務列表過長的問題。

時間輪實現了在插入和刪除操作中時間複雜度皆為o(1)。

在任務量小的場景下:最小堆實現,可以根據堆頂設定超時時間,陣列儲存結構,節省記憶體消耗,使用最小堆可以得到比較好的效果。而時間輪定時器較為複雜點,由於需要維護乙個執行緒用來撥動指標,且需要開闢乙個bucket陣列,消耗記憶體大,使用時間輪會較為浪費資源。在任務量大的場景下:最小堆的插入複雜度是o(logn), 相比時間輪o(1) 會造成效能下降。更適合使用時間輪實現。

durations表示乙個時間段。不過chrono庫中已經封裝好常用的時間單位,比如hours,miniutes,seconds,milliseconds等,最高精度可到納秒。

time_poitnt顧名思義,表示乙個時間點。常用函式有

now()  - 獲取當前時間
chrono庫提供了三個時鐘,分別為

system_clock - 系統提供的實時時鐘,精度為微秒。

high_resolution_clock - 當前系統時鐘週期最短的時鐘,精度為納秒。

steady_clock - 不會被調整的單調時鐘,精度為納秒。

為了便於閱讀,我使用using建立了別名。

using timeoutfuction = std::function<

void()

>

;using ms = std::chrono::milliseconds;

using clock = std::chrono::high_resolution_clock;

using timepoint = clock::time_point;

其中最初設計時優先佇列並非存貯timer指標,而是直接儲存物件。但是在addtimer函式中發現乙個問題。直接使用timer的話。addtimer函式返回值則是返回新生成timer物件的引用。但是在多執行緒操作中可能會使引用的內容提前被釋放,導致程式錯誤。

這個定時器我打算使用到自己的簡易http伺服器。tiny_http_server。

timer.h標頭檔案

#pragma once

#include

#include

#include

#include

#include

using timeoutfuction = std::function<

void()

>

;using ms = std::chrono::milliseconds;

using clock = std::chrono::high_resolution_clock;

using timepoint = clock::time_point;

class

timer

;void

setused

(bool used)

;bool

isused()

const

;void

runtimeoutfunction()

const

; timepoint getexpiretime()

const

;bool

operator

<

(const timer& a)

private

: timepoint expiretime_;

timeoutfuction timeoutfuction_;

bool used_;};

struct timercmp };

class

timermanager;~

timermanager()

=default

;void

updatetime()

; std::shared_ptr

addtimer

(const

int&time , timeoutfuction timeoutfun)

;//返回引用 還是指標 是個問題

void

deltimer

(std::shared_ptr timer)

;//因為優先佇列只能刪除頂部,使用惰性刪除,減少開銷,真正刪除在tick()和getexpiretime()

void

tick()

;//心跳函式

intgetexpiretime()

;//獲取超時時間

private

: std::priority_queue

, std::vector>

,timercmp> mangerqueue_;

//timer過載<,生成最小堆

timepoint nowtime_;

std::mutex lock_;

};

timer.cpp

timer.cpp

#include

"timer.h"

std::shared_ptr timermanager::

addtimer

(const

int& time, timeoutfuction timeoutfun)

return timer;

}void timermanager::

deltimer

(std::shared_ptr timer)

}void timermanager::

tick()

if(std::chrono::duration_cast

(mangerqueue_.

top()-

>

getexpiretime()

- nowtime_)

.count()

>0)

//沒有超時

return

; mangerqueue_.

top()-

>

runtimeoutfunction()

; mangerqueue_.

pop();

}}}int timermanager::

getexpiretime()

if(mangerqueue_.

empty()

)return expiretime;

expiretime = std::chrono::duration_cast

(mangerqueue_.

top()-

>

getexpiretime()

- nowtime_)

.count()

; expiretime <0?

0: expiretime;

return expiretime;

}}

乙個精確的定時器

typedef union large integer longlong quadpart 8位元組整型數 large integer large integer litmp longlong qpart1,qpart2 double dfminus,dffreq,dftim queryperfor...

基於時間輪的定時器

目錄 是乙個單層時間輪,當指標走到某一格上,就獲取那一格上掛的任務將其執行。當時如果時間跨度大的時候,格仔數明顯不夠,那麼就可以做成多級時間輪。其實就是當低層的時間輪走了一圈,將它高一層的時間輪走一格,並且將掛在高層時間輪上的任務分配下來。檔案 include timewheel.h include...

iOS 在每乙個cell上新增乙個定時器的方案

1 首先建立乙個陣列,用來建立所有的定時器的時間 nsmutablearray totallasttime return totallasttime 2 當從網路請求過來時間之後,迴圈遍歷,行數和時間作為key,將值作為value放進字典中放進陣列 所有剩餘的時間 lasttime for int ...