日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

通常來講,就是利用 select 的空余時間,來進行時鐘檢查,不管是 select / poll / epoll/ kevent,以下統稱 select,它有一個等待時間作為參數,即沒有事件時,最多 wait 多少時間,我們把這個作為網絡庫的基準頻率,比如 10MS,或者 20MS, 25MS, 50MS,都是常用的幾個值。

就是說網絡庫調用 select 等待事件時如果沒有事件,那么最長等待 10MS 就返回了,這時再處理完所有網絡事件后,就可以來處理時鐘數據了。事件處理函數就是這樣:

def update_events(milisec = 10):
result = selector.select(milisec)
for fd, event in result:
do something with socket event
current = time.time()
update_timer(current)

while 1:
WAIT_MILLISEC = 10
update_events(WAIT_MILLISEC)

關鍵就是這個兩次 select 中間 update_timer 的任務:集合中檢查需要喚醒的時鐘,并且調用它們的回調函數,來驅動整個服務器的時鐘運行,以最簡單的掃描法為例:

def update_timer (current):
for timer in available_timers:
while current >= timer.expires:
timer.callback(current)
timer.expires += timer.period

available_timers 記錄著當前可用的所有 timer 的集合,expires 是他們需要被觸發的時間,如果當前時間大于等于這個 expires,認為該 timer 需要被觸發到。注意 timer.expires 更新的時候是 += 周期,而不是 = current + 周期,后者會導致誤差積累,長時間運行后偏差越來越大。同時這里需要 while,因為可能跨越兩個以上周期,當然只運行一次的 timer 就不需要了,這里只是簡化下。

比如 libevent 里面的主循環 event_base_loop 每次 select 完畢后就調用一次 timeout_process。

這就是 Timer 調度的基本原理。

可能你會發現每次 select 結束都要掃描整個 available_timers 集合,是一個非常費時間的事情,那么首先想到的就是優先隊列了:將 Timer 節點按照 expires 的先后順序,將最快要發生的超時節點放在前面,每次檢測隊列頭就可以判斷是否超時了。

 

比如 libevent 里面的 timerout_process 函數,就是用最小堆來存儲超時事件,每次檢測堆的第一個節點如果超時則刪除并繼續檢測下一個,否則跳出循環(此時沒有任何節點到期)。

還有一種固定超時隊列,就是里面的節點的超時周期都是相同的,那么每次增加都在最后,每次檢測都只檢測頭部。比如所有鏈接都要檢測60秒無事件超時這個事情,就可以用它,因為60秒是固定的,新增時放到隊列最后,檢測時只檢測頭部是否超時,如果有事件來到,就刪除并重新加入隊列末尾,這是固定超時隊列。

還有上面專門說的系統提供的 timerfd,創建后加入 select, 但是受限于 linux 系統,跨平臺就用不了了,不能太依賴。

然而這些都不算最完美的解決方案,一旦超時節點多達上萬個,每個時間都不同,又考慮通用實現(非特定平臺實現)的話,這幾種調度方式是要吃虧的,目前最好的算法是 Linux Kernel 的時間輪算法,幾乎保證不管有多少個時鐘對象要處理,每次 update_timer 的時間都幾乎是常數。

具體可以看代碼 kernel/timer.c ,時間輪的應用層實現見我寫的:
Asyn.NET/itimer.h at master · skywind3000/AsyncNet · Github
AsyncNet/itimer.c at master · skywind3000/AsyncNet · GitHub

兩個文件,拷貝走就得了,沒有任何第三個文件的依賴,更沒有全局唯一的時鐘管理器。

一般來講 “侵入式” 的網絡庫,都會把這個事情給管理了,因為他們接管你的主循環,比如 libevent, skynet 之類,你進入一個 event_loop 就只有程序結束才能出來了,而非侵入式的網絡庫不會接管你的主循環,可以讓你自己主動去觸發。

比如某同事想在 libevent 上跑一套自己的時鐘系統,而 event_base_dispatch 屬于進去就出不來的 “侵入式” 設計。為了能在 select 前后加入自己的時鐘調度,不得不把 libevent 改出一個 branch 來,所以 侵入式 是比較惡劣的設計。libevent 應該學習一下 win32,把 GetMessage, DispatchMessage 放出來,比如叫做:

int event_base_update(struct event_base *base, int max_wait_time);

讓你主動去觸發它,然后靈活的在前后做點事情,還可以用 event_base_wakeup 從另外一個現成喚醒它,這樣就會靈活很多了,完全取代 event_base_dispatch 這個進去出不來的死循環。

每次 select wait 的時間一般用一個固定值,稱為一個 TICK,固定值選大了,時鐘基準周期就會很長,短時誤差就會增大,選小了,又會占用額外 cpu,可以模擬 Linux 使用 100Hz的值,即 10ms來做,這也是通行做法。

不嫌麻煩還可以每次從 timer 集合里面選擇最先要超時的事件,計算還有多長時間就會超時,作為 select wait 的值,每次都不一樣,每次都基本精確,同時不會占用多余 cpu,這叫 tickless,Linux 的 3.x以上版本也支持 tickless 的模式來驅動各種系統級時鐘,號稱更省電更精確,不過需要你手動打開,FreeBSD 9 以后也引入了 tickless。

TICKLESS 模式可以說是一個新的方向,但是目前處于默認關閉的測試狀態,那么你的網絡庫到底是用 TICK 還是 TICKLESS,看你根據具體情況來評估了。

分享到:
標簽:網絡
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定