# Python: 可中斷的 Sleep

在 Python 裡面，有個很好用的函數，叫做 `time.sleep` ，他可以讓程式暫停幾秒，不過要讓程式更好，需要再對 “Sleep” 更了解一些。

# 用途

就我來說，會用到的情況不外乎這幾種：

1. 需要 polling 的等待函數
    
2. 定期執行程式的 cronjob
    
3. 暫時性的等待資源的 work around。
    

```python
# 只是一個 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，然後定期跑一個函數。

```python
def main():  
    while True:  
        time.sleep(??)  
        do_job()
```

然後，我們希望在送 `SIGTERM` 時，可以中斷程式結束，當然，在 `do_job` 時收到 `SIGTERM`的話，處理方式也視情況，看是直接中斷或者等這一輪處理完再結束。這裏先當要跑完完整的一輪。

time.sleep 雖然在的介面上並沒有提供任何中斷方式。但是根據[官方文件](https://docs.python.org/3/library/time.html#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】來的乾淨。

# 參考

* [https://mozillazg.com/2017/07/python-time-sleep-terminate-by-signal.html](https://mozillazg.com/2017/07/python-time-sleep-terminate-by-signal.html)
