亚洲熟女综合色一区二区三区,亚洲精品中文字幕无码蜜桃,亚洲va欧美va日韩va成人网,亚洲av无码国产一区二区三区,亚洲精品无码久久久久久久

sleep()到底睡多久,你知道嗎?

sleep()到底睡多久,你知道嗎?

丁鐸

 


2014年畢業(yè)加入騰訊,對(duì)終端的性能測(cè)試有豐富的經(jīng)驗(yàn),《Android移動(dòng)性能實(shí)戰(zhàn)》作者之一,現(xiàn)在從事后臺(tái)的性能測(cè)試。

***本文共2008個(gè)字,閱讀需要5分鐘,本文經(jīng)授權(quán)轉(zhuǎn)自騰訊藍(lán)鯨(微信號(hào):Tencent_lanjing)

1. 背景

最近負(fù)責(zé)一個(gè)很簡(jiǎn)單的需求:在服務(wù)器上起一個(gè)后臺(tái)進(jìn)程,每隔10秒鐘上報(bào)一下CPU、內(nèi)存等信息。就是這么簡(jiǎn)單的需求,發(fā)生了一個(gè)有趣的問題。

通過數(shù)據(jù)庫查看上報(bào)的數(shù)據(jù),發(fā)現(xiàn)Windows服務(wù)器在5月24號(hào)14:59到15:12之間,少上報(bào)了一個(gè)數(shù)據(jù),少上報(bào)的數(shù)據(jù)會(huì)用null填充。但是看子機(jī)上的日志,這段時(shí)間均是按照預(yù)設(shè)的間隔成功上報(bào),那問題出在哪兒呢?

開發(fā)一時(shí)也是一臉茫然,建議把測(cè)試時(shí)間調(diào)長,看是否能找到規(guī)律。好吧,那就把測(cè)試時(shí)間改到19個(gè)小時(shí),這下還真的發(fā)現(xiàn)了一點(diǎn)規(guī)律。

sleep()到底睡多久,你知道嗎?

上圖第一列是這段時(shí)間上報(bào)的數(shù)據(jù)點(diǎn)序列,即第95個(gè)點(diǎn),第419個(gè)點(diǎn),第二列是上報(bào)的信息,把所有的null過濾出來,看到相鄰行的序號(hào)相差都在320~330之間,換算成時(shí)間,就是大概55分鐘會(huì)少上報(bào)一個(gè)數(shù)據(jù)。

2. 原因排查

雖然找到了掉點(diǎn)的規(guī)律,但是從子機(jī)日志看都是上報(bào)成功的,是因?yàn)樽訖C(jī)在這段時(shí)間就少采集了一個(gè)點(diǎn)嗎?如果是這樣的話,那么每次采樣周期應(yīng)該是超過10s的。

下面是信息采集上報(bào)的主循環(huán)代碼,這里m_iInterval為5,也就是在每個(gè)采樣周期內(nèi),這個(gè)循環(huán)會(huì)執(zhí)行兩次,然后上報(bào)這兩次中最大的值。

int nmISensor::execute(){    m_iInterval = INTERVAL;while(m_bFlag){updateData();Sleep(m_iInterval*1000);}m_acq=1;return SUCCEED;}

2.1 猜測(cè)1:updateData()有耗時(shí),導(dǎo)致整個(gè)循環(huán)周期的時(shí)間大于預(yù)期

從上面的代碼可以看出,每個(gè)上報(bào)周期,代碼的執(zhí)行邏輯如下示意圖,我們第一反應(yīng)是updateData()的執(zhí)行肯定也會(huì)耗時(shí),那么會(huì)導(dǎo)致整個(gè)采樣周期大于10s。一段時(shí)間后,就會(huì)少上報(bào)一個(gè)數(shù)據(jù),幸福好像來的太突然。

sleep()到底睡多久,你知道嗎?

為了驗(yàn)證這個(gè)猜想,我們統(tǒng)計(jì)了一下updataData()的耗時(shí),統(tǒng)計(jì)的結(jié)果看updateData()耗時(shí)都是0,也就是updateData()基本上是不耗時(shí)的,事實(shí)和我們預(yù)想的并不一樣。

2.2 猜想2:Sleep()有誤差

排除了updeData()的原因,現(xiàn)在只能把目光聚焦在Sleep函數(shù)上,難道是Windows的Sleep函數(shù)實(shí)際休眠的時(shí)間和預(yù)期有差異?為了驗(yàn)證這個(gè)猜想,我們又在日志中打出了Sleep實(shí)際執(zhí)行的耗時(shí)和預(yù)期之間的差異。

這次好像看到了希望,從輸出的日志看,Sleep最終休眠的時(shí)間會(huì)比預(yù)期多15ms,這樣以來,每個(gè)上報(bào)周期就會(huì)多30ms,也就是在55分鐘內(nèi)可以上報(bào)330個(gè)點(diǎn),現(xiàn)在只能上報(bào)329個(gè)點(diǎn)。

sleep()到底睡多久,你知道嗎?

那么問題來了,為什么在Windows上Sleep()會(huì)比預(yù)期的多15ms呢?

我們知道Windows操作系統(tǒng)基于時(shí)間片來進(jìn)行任務(wù)調(diào)度的,Windows內(nèi)核的時(shí)鐘頻率為64HZ,也就是每個(gè)時(shí)間片是15.625同時(shí)Windows也是非實(shí)時(shí)操作系統(tǒng)。對(duì)于非實(shí)時(shí)操作系統(tǒng)來說,低優(yōu)先級(jí)的任務(wù)只有在子機(jī)的時(shí)間片結(jié)束或者主動(dòng)掛起時(shí),高優(yōu)先級(jí)的任務(wù)才能被調(diào)度。下圖直觀地展示了兩類操作系統(tǒng)的區(qū)別。

sleep()到底睡多久,你知道嗎?

sleep()到底睡多久,你知道嗎?

MSDN 上對(duì)Sleep()的說明:Sleep()需要依賴內(nèi)核的時(shí)間片,如果休眠時(shí)間在1~2時(shí)間片之間,那么最終等待的時(shí)間會(huì)是1個(gè)或者2個(gè)時(shí)間片,也就是Sleep()會(huì)有0-15.625(1個(gè)時(shí)間片)的誤差,那么到這里我們的問題也就弄清楚了。

sleep()到底睡多久,你知道嗎?

3. 解決方案

3.1 官方方案

微軟官方針對(duì)Sleep耗時(shí)不精確的問題,也給出相應(yīng)的解決方案:

  1. 調(diào)用timeGetDevCaps獲取時(shí)鐘定時(shí)器能支持的最小粒度

  2. 在定時(shí)開始之前調(diào)用timeBeginPeriod,這樣會(huì)把時(shí)鐘定時(shí)器設(shè)置為最小的粒度

  3. 在定時(shí)結(jié)束之后調(diào)用 timeEndPeriod,恢復(fù)時(shí)鐘定時(shí)器的粒度

同時(shí),官方文檔也指出timeBeginPeriod會(huì)對(duì)系統(tǒng)時(shí)鐘、系統(tǒng)耗電和任務(wù)調(diào)度有影響,也就是timeBeginPeriod雖好,當(dāng)不能濫用。

3.2 開發(fā)的方案

開發(fā)最后沒有采用官方給的方案, 畢竟頻繁調(diào)用timeBeginPeriod,帶來的影響很難預(yù)估。而是采用了比較巧妙的方法:本次等待時(shí)長會(huì)減去上次多等的時(shí)間,即如果上次多等了15ms,那么下次只用等4895ms就可以了,這樣可以保證每次循環(huán)周期是10s。

dwStart = GetTickCount();Sleep(dwInterval);dwDiff = GetTickCount() - dwStart - dwInterval;dwInterval = m_iInterval*1000;if (((long)dwDiff > 0) && (dwDiff < dwInterval)){dwInterval -= dwDiff;}

寫到這里,問題已經(jīng)解決,這時(shí)又有個(gè)疑惑涌上心頭,Linux服務(wù)器上有同樣的上報(bào)功能,為什么Linux子機(jī)沒有這個(gè)問題呢?難道Linux對(duì)應(yīng)的開發(fā)是大嬸,已了然這一切?

4. Linux系統(tǒng)上sleep()是怎樣的呢?

找到了Linux上對(duì)應(yīng)的代碼,原來這個(gè)開發(fā)哥并沒有像Windows的開發(fā)哥那樣自己去寫一個(gè)定時(shí)的任務(wù)調(diào)度,而是用了一個(gè)開源的任務(wù)調(diào)度庫APScheduler,才免遭遇難??磥磉@里的奧秘都在這個(gè)開源庫中,接著就去看看APScheduler是怎樣做任務(wù)調(diào)度的。 APScheduler主循環(huán)的代碼如下,紅框圈出了一行關(guān)鍵的代碼,這行代碼的意思是:本次任務(wù)執(zhí)行完成之后,在下次任務(wù)開始前需要等待wait_sechonds的時(shí)間。

sleep()到底睡多久,你知道嗎?

而self._wakeup是一個(gè)Event的對(duì)象,而Event正是Python系統(tǒng)庫threading 中定義的。而Event常用來做多線程的同步。

def __init__(self, gconfig={}, **options):    self._wakeup=Event()

官網(wǎng)對(duì)Event.wait()的解釋:調(diào)用wait()之后,線程會(huì)一直阻塞,直到內(nèi)部的flag設(shè)置為true,或者超時(shí)。在沒有別的線程設(shè)置internal flag時(shí),wait()就可以起到一個(gè)定時(shí)器的作用。

wait([timeout])Block until the internal flag is true. If the internal flag is true on entry, return immediately. Otherwise, block until another thread calls set() to set the flag to true, or until the optional timeout occurs.

那么問題又來了,Event.wait()如果用作定時(shí)器,誤差是多少呢?寫個(gè)demp驗(yàn)證一下。

從測(cè)試數(shù)據(jù)看,Event.wait()取100次的平均偏差為0.1ms,而time.sleep()的平均偏差為7.65ms,看起來Event.wait()精度更高。

sleep()到底睡多久,你知道嗎?

這里再回到我們第一個(gè)猜想,其實(shí)我們的猜想是合情合理的,如果updateData()的耗時(shí)較長,整個(gè)循環(huán)周期必定會(huì)超過預(yù)定的值,所以這里的實(shí)現(xiàn)并不嚴(yán)謹(jǐn),而 APScheduler則是通過起一個(gè)新的線程去執(zhí)行任務(wù),并不會(huì)阻塞循環(huán)周期,可以看到APScheduler在這里處理的還是很合理的。

sleep()到底睡多久,你知道嗎?

5. 結(jié)論

啰嗦了這么多,總結(jié)一下上面的內(nèi)容:

  1. sleep()在Windows和Linux系統(tǒng)上和預(yù)設(shè)值都會(huì)存在一個(gè)偏差,偏差最大為1個(gè)時(shí)間片的時(shí)間;

  2. Event.wait()用來做定時(shí)器精度會(huì)更高,可以達(dá)到0.1ms;

  3. APScheduler看起來是個(gè)不錯(cuò)的任務(wù)調(diào)度庫。


“Linuxy云計(jì)算25期開班倒計(jì)時(shí)5天”

sleep()到底睡多久,你知道嗎?

相關(guān)新聞

歷經(jīng)多年發(fā)展,已成為國內(nèi)好評(píng)如潮的Linux云計(jì)算運(yùn)維、SRE、Devops、網(wǎng)絡(luò)安全、云原生、Go、Python開發(fā)專業(yè)人才培訓(xùn)機(jī)構(gòu)!