新聞中心
有時(shí)候,我們想實(shí)現(xiàn)一個(gè)非常簡(jiǎn)單的定時(shí)功能,例如讓一個(gè)程序每天早上8點(diǎn)調(diào)用某個(gè)函數(shù)。但我們又不想安裝任何第三方庫(kù),也不會(huì)使用 crontab 或者任務(wù)計(jì)劃功能,就想使用純 Python 來(lái)實(shí)現(xiàn)。

可能有同學(xué)會(huì)這樣寫代碼:
- import time
- import datetime
- def run():
- print('我是需要被每天調(diào)用的函數(shù)')
- def schedule():
- target_time = datetime.time(8, 0, 0)
- today = datetime.date.today()
- target_date = today + datetime.timedelta(days=1)
- target_datetime = datetime.datetime.combine(target_date, target_time)
- now = datetime.datetime.now()
- delta = (target_datetime - now).total_seconds()
- time.sleep(delta)
- run()
- while True:
- time.sleep(24 * 3600)
- run()
- if __name__ == '__main__':
- schedule()
這段程序,首先計(jì)算出現(xiàn)在距離明天早上8點(diǎn)相差的秒數(shù)。睡這么多秒以后,第一次運(yùn)行目標(biāo)函數(shù)。然后進(jìn)入一個(gè)死循環(huán),每隔86400秒,程序調(diào)用一次 run 函數(shù)。
這個(gè)程序初看起來(lái),似乎沒有什么問題。但如果你每天觀察它的運(yùn)行時(shí)間,你會(huì)發(fā)現(xiàn)隨著時(shí)間的推移,時(shí)間會(huì)越來(lái)越不準(zhǔn)確。
這是因?yàn)?,run 函數(shù)不是一瞬間就運(yùn)行完成的。它運(yùn)行也會(huì)消耗時(shí)間。假設(shè)程序第一次運(yùn)行 run 函數(shù)的時(shí)候,確實(shí)剛剛好是8:00,run 函數(shù)運(yùn)行了2秒。那么,程序睡眠86400秒以后,時(shí)間實(shí)際上是8:00:02.從第二天開始,每天晚2秒鐘。一個(gè)月就會(huì)晚一分鐘。
但實(shí)際上,我們?nèi)绻冻鲆稽c(diǎn)點(diǎn)微不足道的代價(jià),我們就可以防止這種誤差的發(fā)生,并且程序代碼會(huì)變得更簡(jiǎn)單:
- import time
- import datetime
- def run():
- print('我是需要被每天調(diào)用的函數(shù)')
- def schedule():
- last_run = None
- while True:
- now = datetime.datetime.now()
- if now.strftime('%H:%M') == '08:00' and last_run != now.date():
- run()
- last_run = now.date()
- time.sleep(1)
- if __name__ == '__main__':
- schedule()
程序在一個(gè)死循環(huán)中,每秒做一次檢查,如果當(dāng)前的時(shí)分正好是08:00,并且上一次運(yùn)行不是今天,那么就調(diào)用 run 函數(shù),并把上一次運(yùn)行的時(shí)間設(shè)置為今天。否則,就睡眠1秒鐘。
這樣做,相當(dāng)于每秒都會(huì)校對(duì)時(shí)間,從而避免了長(zhǎng)時(shí)間運(yùn)行導(dǎo)致的時(shí)間誤差。雖然看起來(lái)這個(gè)死循環(huán)會(huì)非常消耗 CPU,但只要你算一下,實(shí)際上它只不過(guò)每天循環(huán)86400次而已。這個(gè)次數(shù)并不多。
但無(wú)論如何,專業(yè)的事情應(yīng)該交由專業(yè)的工具來(lái)做。time.sleep用來(lái)設(shè)置周期性的時(shí)間間隔可以,但它實(shí)際上不適合用來(lái)做定時(shí)任務(wù)。
因?yàn)橐粋€(gè)支持定時(shí)任務(wù)的庫(kù),例如 Python 的schedule或者APScheduler,他們?cè)诖_保定時(shí)時(shí)間準(zhǔn)確上,做了很多工作。還有一些庫(kù)甚至用到了時(shí)間輪這樣的數(shù)據(jù)結(jié)構(gòu)來(lái)確保時(shí)間的準(zhǔn)確性。這不是我們簡(jiǎn)單用兩三行 Python 代碼就能完成的。
總結(jié)
如果能用 crontab 或者任務(wù)計(jì)劃,那么這是最優(yōu)選擇。其次,使用 Python 專用的定時(shí)模塊。最次,才是使用 time.sleep 來(lái)實(shí)現(xiàn)。如果不得不用 time.sleep,那么應(yīng)該盡量縮短檢查的間隔,避免長(zhǎng)時(shí)間睡眠。
本文轉(zhuǎn)載自微信公眾號(hào)「未聞Code」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系未聞Code公眾號(hào)。
分享名稱:為什么不建議使用Time.Sleep實(shí)現(xiàn)定時(shí)功能?
URL分享:http://fisionsoft.com.cn/article/djdissj.html


咨詢
建站咨詢
