最近我发布了一个关于如何推迟Python函数执行的问题(类似于Javascript的setTimeout
),结果使用threading.Timer
是一个简单的任务(只要该函数不与其他代码共享状态,但在任何事件驱动环境中都会创建问题)。
现在我正在尝试做得更好,模拟setInterval
。对于那些不熟悉Javascript的人来说,setInterval
允许每x秒重复调用函数,而不会阻塞其他代码的执行。我已经创建了这个示例装饰器:
import time, threading
def setInterval(interval, times = -1):
# This will be the actual decorator,
# with fixed interval and times parameter
def outer_wrap(function):
# This will be the function to be
# called
def wrap(*args, **kwargs):
# This is another function to be executed
# in a different thread to simulate setInterval
def inner_wrap():
i = 0
while i != times:
time.sleep(interval)
function(*args, **kwargs)
i += 1
threading.Timer(0, inner_wrap).start()
return wrap
return outer_wrap
以下用法
@setInterval(1, 3)
def foo(a):
print(a)
foo('bar')
# Will print 'bar' 3 times with 1 second delays
看起来对我来说它运行得很好。我的问题是:
- 它似乎过于复杂,我担心可能错过了更简单/更好的机制
- 装饰器可以在没有第二个参数的情况下被调用,在这种情况下,它会一直进行下去。当我说永远时,我的意思是永远——即使从主线程中调用
sys.exit()
也无法停止它,按下Ctrl+c
也不行。唯一停止它的方法是从外部杀死Python进程。我想能够从主线程发送信号来停止回调。但我是一个新手,不知道如何在线程之间通信?
编辑如果有人想知道,这是装饰器的最终版本,感谢jd的帮助。
import threading
def setInterval(interval, times = -1):
# This will be the actual decorator,
# with fixed interval and times parameter
def outer_wrap(function):
# This will be the function to be
# called
def wrap(*args, **kwargs):
stop = threading.Event()
# This is another function to be executed
# in a different thread to simulate setInterval
def inner_wrap():
i = 0
while i != times and not stop.isSet():
stop.wait(interval)
function(*args, **kwargs)
i += 1
t = threading.Timer(0, inner_wrap)
t.daemon = True
t.start()
return stop
return wrap
return outer_wrap
它可以像上面那样使用固定的重复次数
@setInterval(1, 3)
def foo(a):
print(a)
foo('bar')
# Will print 'bar' 3 times with 1 second delays
或者可以一直运行,直到收到停止信号
import time
@setInterval(1)
def foo(a):
print(a)
stopper = foo('bar')
time.sleep(5)
stopper.set()
# It will stop here, after printing 'bar' 5 times.
function
,直到调用stopper.set()
,此时循环会突然加速!我检查了一下,确实,如果在上面的示例中在stopper.set()
之后再添加另一个time.sleep(5)
,那就是发生的事情。你需要加入一些逻辑来确保循环在stop.is_set()
为真时停止--例如while i != times and not stop.is_set():
。 - senderlewait()
方法的一个段落,并且没有发现我的测试中的问题。我现在会纠正代码。 - AndreasetInterval()
而不使用线程。答案链接为:https://dev59.com/4sKRzogBFxS5KdRjmcet#14040516 - jfscall_repeatedly(interval, func, *args)
实现。请参考链接:https://dev59.com/52Eh5IYBdhLWcg3wXCZL#22498708 - jfs