【问题标题】:Equivalent of setInterval in pythonpython中setInterval的等价物
【发布时间】: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


【解决方案1】:

您的解决方案对我来说很好。

有几种方法可以与线程通信。要命令线程停止,您可以使用threading.Event(),它有一个wait() 方法,您可以使用它来代替time.sleep()。

stop_event = threading.Event()
...
stop_event.wait(1.)
if stop_event.isSet():
    return
...

要在程序终止时退出线程,请在调用start() 之前将其daemon 属性设置为True。这也适用于Timer() 对象,因为它们是threading.Thread 的子类。见http://docs.python.org/library/threading.html#threading.Thread.daemon

【讨论】:

  • 太棒了!我能够消除对时间模块的依赖并设法进行通信。现在可以轻松停止回调。 :-)
【解决方案2】:

也许这些是 python 中最简单的 setInterval 等价物:

 import threading

 def set_interval(func, sec):
    def func_wrapper():
        set_interval(func, sec) 
        func()  
    t = threading.Timer(sec, func_wrapper)
    t.start()
    return t

【讨论】:

    【解决方案3】:

    也许更简单一点是使用对 Timer 的递归调用:

    from threading import Timer
    import atexit
    
    class Repeat(object):
    
        count = 0
        @staticmethod
        def repeat(rep, delay, func):
            "repeat func rep times with a delay given in seconds"
    
            if Repeat.count < rep:
                # call func, you might want to add args here
                func()
                Repeat.count += 1
                # setup a timer which calls repeat recursively
                # again, if you need args for func, you have to add them here
                timer = Timer(delay, Repeat.repeat, (rep, delay, func))
                # register timer.cancel to stop the timer when you exit the interpreter
                atexit.register(timer.cancel)
                timer.start()
    
    def foo():
        print "bar"
    
    Repeat.repeat(3,2,foo)
    

    atexit 允许使用 CTRL-C 发出停止信号。

    【讨论】:

    • 感谢您的建议,但我已经接受了jd的回答。无论如何,我更喜欢休眠而不是递归地创建新的定时器——顺便说一下,这会产生无限多的线程,还是在函数返回时线程被杀死,即使他们创建的线程仍然打开?
    • AFAIK 它将产生 rep-many 线程,它们都会在最后一个 Timer 完成时终止。
    • 如果你想重复这个函数数千次,甚至是永远,这可能会很烦人。无论如何,感谢您的输入! :-)
    【解决方案4】:

    这个类间隔

    class ali:
        def __init__(self):
            self.sure = True;
    
        def aliv(self,func,san):
            print "ali naber";
            self.setInterVal(func, san);
    
        def setInterVal(self,func, san):
    
            # istenilen saniye veya dakika aralığında program calışır. 
            def func_Calistir():
    
                func(func,san);  #calışıcak fonksiyon.
            self.t = threading.Timer(san, func_Calistir)
            self.t.start()
            return self.t
    
    
    a = ali();
    a.setInterVal(a.aliv,5);
    

    【讨论】:

      猜你喜欢
      • 2011-02-11
      • 1970-01-01
      • 2018-09-06
      • 2013-02-06
      • 2012-03-22
      • 2016-12-27
      • 2013-12-13
      • 2021-06-04
      相关资源
      最近更新 更多