一日一技:為什麼不建議使用 time.sleep 實現定時功能?

2021-01-18 未聞Code

有時候,我們想實現一個非常簡單的定時功能,例如讓一個程序每天早上8點調用某個函數。但我們又不想安裝任何第三方庫,也不會使用 crontab 或者任務計劃功能,就想使用純 Python 來實現。

可能有同學會這樣寫代碼:

import time
import datetime


def run():
    print('我是需要被每天調用的函數')

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()

這段程序,首先計算出現在距離明天早上8點相差的秒數。睡這麼多秒以後,第一次運行目標函數。然後進入一個死循環,每隔86400秒,程序調用一次 run 函數。

這個程序初看起來,似乎沒有什麼問題。但如果你每天觀察它的運行時間,你會發現隨著時間的推移,時間會越來越不準確。

這是因為,run 函數不是一瞬間就運行完成的。它運行也會消耗時間。假設程序第一次運行 run 函數的時候,確實剛剛好是8:00,run 函數運行了2秒。那麼,程序睡眠86400秒以後,時間實際上是8:00:02.從第二天開始,每天晚2秒鐘。一個月就會晚一分鐘。

但實際上,我們如果付出一點點微不足道的代價,我們就可以防止這種誤差的發生,並且程序代碼會變得更簡單:

import time
import datetime


def run():
    print('我是需要被每天調用的函數')

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()

程序在一個死循環中,每秒做一次檢查,如果當前的時分正好是08:00,並且上一次運行不是今天,那麼就調用 run 函數,並把上一次運行的時間設置為今天。否則,就睡眠1秒鐘。

這樣做,相當於每秒都會校對時間,從而避免了長時間運行導致的時間誤差。雖然看起來這個死循環會非常消耗 CPU,但只要你算一下,實際上它只不過每天循環86400次而已。這個次數並不多。

但無論如何,專業的事情應該交由專業的工具來做。time.sleep用來設置周期性的時間間隔可以,但它實際上不適合用來做定時任務。

因為一個支持定時任務的庫,例如 Python 的schedule或者APScheduler,他們在確保定時時間準確上,做了很多工作。還有一些庫甚至用到了時間輪這樣的數據結構來確保時間的準確性。這不是我們簡單用兩三行 Python 代碼就能完成的。

總結

如果能用 crontab 或者任務計劃,那麼這是最優選擇。其次,使用 Python 專用的定時模塊。最次,才是使用 time.sleep 來實現。如果不得不用 time.sleep,那麼應該儘量縮短檢查的間隔,避免長時間睡眠。

更多開發中的坑與避坑知識點,請關注我的課程《Python 業務開發 常見錯誤案例集》:


Long-press QR code to transfer me a reward

攢錢給產品經理買房。

As required by Apple's new policy, the Reward feature has been disabled on Weixin for iOS. You can still reward an Official Account by transferring money via QR code.

相關焦點

  • 英語「使用能力」訓練:Sleep on it
    有個「英語教學」視頻是要求你的「見到英語說出中文」(Put English into Chinese)的,而不是「見英語說出英語」(Put English into English)的英語「使用能力」訓練:你用中文是「懂」了,可是你的英語能力沒有一點改變和使用。1. sleep on it.
  • Evolution of Sleep
    prey, which are in turn much more likely to experience dreamless sleep.The fact that deep dream sleep is rare among prey today seems clearly to be a product of natural selection, and it makes sense that today, when sleep is highly evolved, the stupid animals are less frequently
  • python:倒計時功能,記錄時間功能;常用方法!
    importtime#在列印時顯示在終端更新時間倒計時功能# 實際代碼:分鐘轉成秒要乘60,用-1來倒計時。print(info,end="")#在列印時,用'\b'退格符*(len(取info字符串的長度)* 2),並用end=''連接,# 在屏幕上更新列印的內容就得使用flush = True的參數。
  • 英語詞彙:sleep的有關短語
    說到睡覺,我們最熟悉的短語就是sleep。可是,這個單詞的各種搭配,你真的會用嗎?   今天我們就來總結一下跟睡覺和sleep有關的各種表達。   Sleep over   To sleep in another person's home as their guest.
  • sound+sleep助眠儀讓失眠準媽媽「想睡覺」
    調查顯示,88.8%正常婦女睡眠模式在妊娠期有所改變,76%的孕期女性睡眠質量差,其中100%會夜醒,57%失眠,19%有打鼾等睡眠呼吸紊亂症狀,24%有不寧腿症候群。睡眠質量不高的狀態,不可避免地會影響到自身抵抗力,可能容易患上一些疾病,從而間接影響到寶寶的健康。
  • 如何使用老人手機定時開關機
    現在越來越多的手機廠商都會在手機中添加定時開關機的功能,有些老人手機(比如21克手機)也添加了定時開關機的功能,那麼在哪兒找到定時開關機,怎樣使用定時開關機的功能,以及他的意義在於哪兒?下面為大家一一解讀步驟一、定時開關機就是你可以設定手機自動開機和關機的時間,這個功能一般很容易被人遺忘但是卻是很實用的,老年人在使用過程中比如睡覺前常常會忘記關機,這樣一是手機輻射會影響人的身體健康,二是半夜來個騷擾電話那整晚的睡眠就這樣徹底被毀了。給21克老人手機設置了定時開關機就可以避免這樣的狀況發生。
  • 如何使用電源定序器來實現指定的電源軌定時及定序
    本文將詳細介紹電源定序,探討電源定序規範和技術,以及如何使用電源定序器來實現指定的電源軌定時及定序。 為什麼要關注電源定序? FPGA 及類似的複雜集成電路 (IC) 可在內部分解成多個功率域。在啟動或關斷器件時,此類 IC 大多需要特定的順序。
  • 為什麼MySQL不建議delete刪除數據
    我負責的有幾個系統隨著業務量的增長,存儲在MySQL中的數據日益劇增,我當時就想現在的業務方不講武德,搞偷襲,趁我沒反應過來把很多表,很快,很快啊都打到了億級別,我大意了,沒有閃,這就導致跟其Join的表的SQL變得很慢,對的應用接口的response time也變長了,影響了用戶體驗。
  • 如何使用手機控制電腦定時關機
    安卓手機和電腦可以通過安裝手機客戶端和電腦客戶端的方式實現互聯,而且手機可以控制電腦關機重啟等功能,魅族手機的電腦控就可以實現這些功能首先下載電腦控pc端的軟體,解壓到本地後安裝,需要java支持,請先安裝好,壓縮包內有,然後再電腦的右下角有電腦控的常駐程序,右鍵選擇設置
  • TOF測距功能的原理及使用方法
    出色的低功耗性能、片上DC-DC和Time-of-flight使得此晶片功功能強大,可用於智能家居、安全系統、定位追蹤、無線測距、穿戴設備、智能手環與健康管理等等。SX1280支持RSSI,用戶可以根據需要實現深度的二次開發;SX1280亦集成飛行時間(time-of-flight),適用於測距功能。
  • 「睡得晚」可不是「sleep late」,正確的表達是…
    sleep是睡覺,late有遲了、晚了的意思所以睡得晚是sleep late?那就錯過頭了!get to bed early:很早就上床get to sleep late:很晚才睡著(get to sleep=進入睡著的狀態)大家平時都是什麼點睡覺呢?是屬於夜貓子一族還是早睡早起一族?接下來再給大家補充一些與睡眠相關的英語表達。
  • Everything About Sleep
    來自作者的建議。特別提醒:以下的內容均為節選(有刪減),詳情請購買書本閱讀,紙質版和電子版均能在國內選購。(Am I getting enough sleep?)參考的依據有以下幾點:1)當你在早晨醒來之後,你能在早上10點或11點間再睡著嗎?2)你能不依靠咖啡因在中午前狀態良好嗎?3)如果你沒有設置鬧鐘,你會睡過頭嗎?(如果這樣,你需要的睡眠多於給自己的睡眠。)
  • 「睡得晚」可不是「sleep late」!
    很多人認為sleep是睡覺,late可以表達晚的,sleep late 就錯誤的被理解成了睡覺睡的晚,sleep over 就被理解成了「睡過頭」。這些理解完全都是錯誤的!應該怎麼理解?看完今天的文章就明白了!
  • 【TIME】A New Theory on How It Protects Our Brains
    In humans, sleep is punctuated by rapid eye movement (REM) sleep every 90 minutes. This is when most dreaming occurs.
  • It's time to get moving and active again
    As a result, many of us have been less physically active, have had far longer screen time, irregular sleep patterns and worse diets, resulting in for many of us, weight gain and a loss of physical fitness
  • Unity 2020.2 優化了 Time.deltaTime,以實現更流暢的遊戲體驗
    而要實現獨立於幀的運動,則要用到時間增量(Delta Time):  void Update(){transform.position += m_Velocity * Time.deltaTime;}  右滑查看完整代碼  上方代碼可以實現對象以不變的矢量向前運動的效果,且運動會無視遊戲的幀率。
  • 使用AspectJ註解技術實現AOP功能
    AspectJ是一個面向切面編程的框架,使用AspectJ不需要改動Spring配置文件,就可以實現Spring AOP功能。本篇結合實際案例詳細講述使用AspectJ實現AOP功能。通過本篇的學習,可以解決如下問題。● 使用AspectJ技術的背景是什麼?
  • 20課零基礎快速學python代碼模塊的定義 使用自己的模塊
    之前我們學過函數, 我們說函數就如同包裹,它封裝了一段代碼,可以實現所需功能。後來我們學了類,我們發現,類比函數更強大,它封裝了屬性與方法,可以實現更多的功能。總結下if __name__ == '__main__':語句的用法好了,這就是如何使用我們自己的模塊。接下來,我們學習下如何使用他人模塊。運行代碼你會發現,先列印「hello」,延遲3秒後才列印「python」這就是time模塊中的time.sleep(3)語句起作用了。你還使用過Python自帶的random模塊。
  • 連「sleep late」是什麼意思都不知道,怎麼敢稱為熬夜冠軍?
    「go to bed」才是「去睡覺」的意思,「go to sleep」這個說法沒問題,但它指的是「入睡」、「睡著了」,發生在「去睡覺」之後而對於很多人來說,「go to bed」和「go to sleep」之間,差了可能不止一首歌的時間。
  • sleep with the fishes和魚睡在一起是什麼意思?其實指死亡
    sleep with the fishes死亡。經典影片《教父》曾引用這個習語,It means Luca Brasi sleeps with the fishes.意指按黑道的刑罰,把人扔下海餵魚,藉此表示「死亡」。後來也用來比喻抽象的「死定了」。