Python: 可中斷的 Sleep
在 Python 裡面,有個很好用的函數,叫做 time.sleep
,他可以讓程式暫停幾秒,不過要讓程式更好,需要再對 “Sleep” 更了解一些。
用途
就我來說,會用到的情況不外乎這幾種:
需要 polling 的等待函數
定期執行程式的 cronjob
暫時性的等待資源的 work around。
# 只是一個 Polling 陽春的範例
def wait_for_data():
while True:
time.sleep(1)
if poll_ok():
return
最後一種姑且不談,畢竟這上產品線時是通常要修掉的,前面兩個使用時,就需要考慮到一個問題,如何提前“中斷”,例如在 Golang 裡面,就可以用 context 去中斷。Cronjob 假如是用 supervisord
進行管理,那在吃 Signal 時也會要好好的處理:中斷 time.sleep 並且做 Graceful Shutdown。
但是,time.sleep 的介面,並沒有直接提供提前中斷的功能。
Cronjob: Main Thread + Signal Handler
假如,使用情境是 Cronjob,只有 main thread,然後定期跑一個函數。
def main():
while True:
time.sleep(??)
do_job()
然後,我們希望在送 SIGTERM
時,可以中斷程式結束,當然,在 do_job
時收到 SIGTERM
的話,處理方式也視情況,看是直接中斷或者等這一輪處理完再結束。這裏先當要跑完完整的一輪。
time.sleep 雖然在的介面上並沒有提供任何中斷方式。但是根據官方文件:
Changed in version 3.5: The function now sleeps at least secs even if the sleep is interrupted by a signal, except if the signal handler raises an exception.
他可以被 signal 中斷:在 signal 裡面扔出一個特定的 Exception,然後讓 sleep 外面抓這個 Exception。
Cronjob: threading.Event
扔 Exception 仍然不是一個好的方案,他某些程度上來說仍然算髒,所以有第二個方案:使用 threading.Event
當 event 物件被 set 時,wait 會中斷,並且離開迴圈。這樣會比起【期待 time.Sleep 會扔出 signal 扔的 Exception】來的乾淨。