python 如何寫一個定時器,循環定時作某一操做呢?html
from threading import Timer def hello(): print "hello, world" t = Timer(10.0, hello) t.start()
10秒後輸出:python
hello, world
重點研究 t = Timer(10.0, hello)
這句代碼,python 提供了一個Timer 對象,它會在指定的時間後執行某一操做;它的完整形式:多線程
class threading.Timer(interval, function, args=[], kwargs={})
interval
是時間間隔,function
是可調用的對象,args
和 kwargs
會做爲 function
的參數。python2.7
注意:這裏只會執行一次 function,而不會一直定時執行,且 Timer 在執行操做的時候會建立一個新的線程。函數
Timer
在 python2 和 python3 有點區別:線程
# python2.7 def Timer(*args, **kwargs): return _Timer(*args, **kwargs) # python3.7 class Timer(Thread): pass
在 python3,Timer
是 Thread
的子類;在 python2,_Timer
是 Thread
的子類,而 Timer
只是 _Timer
類的工廠方法。code
上面的代碼只會打印一次 hello, world
後退出,那麼如何循環間隔打印呢?htm
一種方法是在 function
裏繼續註冊一個 Timer,這樣就能夠在下一個 interval
繼續執行 function
;對象
from threading import Timer def hello(): print "hello, world" Timer(10.0, hello) .start() t = Timer(10.0, hello) t.start()
每隔 10 秒輸出一個 hello, world
。繼承
達到效果了,可是這裏面好像有點問題。回到 Timer 自己,它是一個 thread,每次循環間隔操做,系統都要建立一個線程,而後再回收,這對系統來講開銷很大。若是時間間隔 interval 很短,系統會一會兒建立不少線程,這些線程很難快速回收,致使系統內存和cpu資源被消耗掉。
因此不提倡在 function 裏繼續註冊一個 Timer。
這裏有更 pythonic 的方法:
from threading import _Timer def hello(): print "hello, world" class RepeatingTimer(_Timer): def run(self): while not self.finished.is_set(): self.function(*self.args, **self.kwargs) self.finished.wait(self.interval) t = RepeatingTimer(10.0, hello) t.start()
重點研究 RepeatingTimer
類,它繼承了 threading._Timer
,可是重寫了父類的 run
方法。這是 Python2 的寫法,python3 中 RepeatingTimer
應該繼承 threading.Timer
。
爲何要重寫 Thread
的 run
方法?
_Timer
是一個 Thread
子類,咱們先看看 Thread
類的 run
用法。
from threading import Thread def hello(): print "hello, world" # 繼承 Thread class MyThread(Thread): # 把要執行的代碼寫到run函數裏面 線程在建立後會直接運行run函數 def run(self): hello() t = MyThread() t.start()
Thread 對象的完整定義:
class threading.Thread(group=None, target=None, name=None, args=(), kwargs={})
其中 run
方法代碼:
class Thread(_Verbose): def run(self): try: if self.__target: self.__target(*self.__args, **self.__kwargs) finally: # Avoid a refcycle if the thread is running a function with # an argument that has a member that points to the thread. del self.__target, self.__args, self.__kwargs
標準的 run
方法用於執行用戶傳入構造函數的 target
方法。 子類能夠重寫 run
方法,把要執行的代碼寫到 run
裏面,線程在建立後,用戶調用 start()
方法會運行 run()
方法。
因此 RepeatingTimer
重寫 _Timer
的 run() 方法,能夠改變線程的執行體,當咱們調用 RepeatingTimer
的 start() 方法時會執行咱們重寫的 run() 方法。
再看看 RepeatingTimer 類中的 while not self.finished.is_set()
語句,self.finished.is_set()
直到 True
纔會退出循環,定時器才結束。finished
是 threading.Event
對象。一個 Event
對象管理着一個 flag 標誌,它能被 set()
方法設置爲 True,也能被 clear()
方法設置爲 False,調用 wait([timeout])
線程會一直 sleep 到 flag 爲 True 或超時時間到達。
咱們知道定時器有一個 cancel()
方法能夠提早取消操做。它實際上是調用 Event.clear()
方法提早讓 wait
方法結束等待,而且判斷在 flag 爲 true 的狀況下不執行定時器操做。具體的代碼:
class _Timer(Thread): """Call a function after a specified number of seconds: t = Timer(30.0, f, args=[], kwargs={}) t.start() t.cancel() # stop the timer's action if it's still waiting """ def __init__(self, interval, function, args=[], kwargs={}): Thread.__init__(self) self.interval = interval self.function = function self.args = args self.kwargs = kwargs self.finished = Event() def cancel(self): """Stop the timer if it hasn't finished yet""" self.finished.set() def run(self): self.finished.wait(self.interval) if not self.finished.is_set(): self.function(*self.args, **self.kwargs) self.finished.set()
因此 RepeatingTimer
的 run 方法會一直執行 while
循環體,在循環體了會執行用戶傳入的 function
對象,並等待指定的時間。當用戶想退出定時器時,只須要調用 cancel
方法,將 flag 置爲 True 便不會繼續執行循環體了。這樣便完成了一個還不錯的循環定時器。