【发布时间】:2013-04-12 18:50:09
【问题描述】:
我最近发布了一个 question 关于如何在 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(): -
你说得对,我误解了文档中关于
wait()方法的一段,我的测试没有发现问题。我现在将更正代码。
标签: python multithreading setinterval