本文解決線程控制的2個場景
1. 線程延時:延遲一定時間,再執行后續程序
2. 兩個線程,當一個線程執行時間超過規定時間時,執行另一個線程
場景1:定時器
具體參考 我的博客 后續會寫
場景2:繼承多線程基類
DelayAction:重寫 run 方法,在 run 中延遲
DelayAction2:重寫 run 方法,在 run 中延遲,並獲取輸出
class DelayAction(threading.Thread): # 延時執行某個函數 def __init__(self, sec, func, *args): threading.Thread.__init__(self) self.sec = sec self.func = func self.args = args def run(self): time.sleep(self.sec) apply(self.func, self.args) class DelayAction2(threading.Thread): # 延時執行某個函數,並獲取返回 def __init__(self, sec, func, *args): threading.Thread.__init__(self) self.sec = sec self.func = func self.args = args self.res = None def run(self): time.sleep(self.sec) self.res = apply(self.func, self.args) if __name__ == '__main__': ### test DelayAction def myfunc(x): print(x) return 1 da = DelayAction(10, myfunc, 222) da.start() # 10s 后打印 222 ### test DelayAction2 da2 = DelayAction2(10, myfunc, 333) da2.start() # 10s 后打印 333 print(da2.res) # 馬上打出 None ,“打印”這個線程和 da2 是並行的,之所以打印出 None,而不是函數值,因為此時函數還沒執行呢 da2.join() print(da2.res) # 帶函數執行完畢后,打印出 1
場景1 和 2:裝飾器實現
timeout:簡單例子,方便對比后續
timeout2:用 定時器延時執行了一個回調函數,然而 被裝飾的函數並沒有被延時,如果加上 t.join 就實現了延時
timeout3:解決了這么一種場景:我需要執行一個函數,如果這個函數某個規定時間內沒有執行結束,則強制結束,類似 web 中的 wait
// 這是一個失敗的例子,在 callback 中 return,並不能使整個函數結束
timeout4:解決了上述場景,添加了線程守護
def timeout(func): # 被裝飾的函數帶參數的裝飾器 def myfunc(sec): time.sleep(sec) func() return myfunc def timeout2(sec, callback): # 裝飾器帶參數的裝飾器 def _deco(func): t = threading.Timer(sec, callback) # threading.Timer定時器,sec 秒后執行回調函數 t.start() t.join() def myfunc(arg): # 注意這里其實並沒有延時 myfunc,這只是一個樣例,你可以根據需要自己擴展,如在 函數上一行加上 t.join() func(arg) return myfunc return _deco def timeout3(sec, callback): # 有這么一種場景:我需要執行一個函數,如果這個函數某個規定時間內沒有執行結束,則強制結束 【這個函數沒有實現需求】 def _deco(func): t = threading.Timer(sec, callback) # callback 假設 10s 結束 t.start() def myfunc(): func() # 被裝飾的函數 假設 100s 結束 return myfunc return _deco def timeout4(callback, arg): # 有這么一種場景:我需要執行一個函數,如果這個函數某個規定時間內沒有執行結束,則強制結束 【這個函數實現了需求】 def _deco(func): t = threading.Thread(target=callback, args=(arg, )) # callback 假設 10s 結束 t.setDaemon(True) ## 必須有, 線程守護,myfunc 相當於主線程 t.start() def myfunc(): func() # 被裝飾的函數 假設 100s 結束 return myfunc return _deco if __name__ == '__main__': ### test timeout @timeout def test(): print(33) # test(10) # 10s 后打印 33 ### test timeout2 def callback(): print('callback2') @timeout2(10, callback) def test2(x): print(x) # test2(100) # 立刻打印出 100, 也就是 test2 立即執行了,並沒有被延時 # 10s 后打印出 callback2,回調函數延時執行 ## timeout2 中如果加上 t.join,10s 后立即先后打印了 callback2 100,也就是雙重延時 ### 注意理解就行,怎么寫看實際需求 ### test timeout3 def callback(): print('callback3') return @timeout3(10, callback) def test3(): time.sleep(100) print('test3') # test3() # 10s 后打印出 callbak2 callback3,100s 后打印出 test3 ## 這里 test2 並沒有執行,為什么打印出 callbak2 callback3,后面我會解釋 ### test timeout4 def callback(sec): time.sleep(sec) print('callback4') return @timeout4(callback, 100) # 這里是線程守護,10s 的線結束,100s 的自動結束,故主線程是時間短的線程 def test4(): time.sleep(10) print('test4') test4()
注意,時間長的線程被守護。
這里對裝飾器簡單總結2點
1. 裝飾器,就是修飾一個函數,所有一定有一層是只輸入一個 func
2. 裝飾器首先執行的是裝飾的函數 deco,並且 deco 永遠是頂層函數 【解決上面 ‘打印出 callbak2 callback3’ 的問題】
import threading def timeout2(sec, callback): # 裝飾器帶參數的裝飾器 def _deco(func): t = threading.Timer(sec, callback) t.start() # t.join() def myfunc(arg): func(arg) return myfunc return _deco def callback(): print('callback2') @timeout2(1, callback) # 立即打印 callback2,並結束 def test2(x): print(x)
並沒有執行任何函數,而 deco 被自動執行。
場景2:線程守護簡單版
import time import threading def func1(): time.sleep(10) print('func1') return 1 def func2(): time.sleep(20) print('func2') return 2 def test(): t2 = threading.Thread(target=func2) # 慢的線程,作為守護線程 t2.setDaemon(True) t2.start() func1() test() # 10s 后打印 func1,當快的線程結束時,慢的線程自動結束
這里可以重寫 run 方法,獲取 func2 的輸出,根據需求自行擴展。
