本文解决线程控制的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 的输出,根据需求自行扩展。