与pythons线程的间隔

时间:2013-12-24 18:33:57

标签: python multithreading python-3.x

我在javascripts中只使用了setInterval类似于python而且它有点不同,问题是我似乎无法弄清楚如何取消它,因为当我取消它时,它只是一个新的一,并在我取消原始帖子后继续运行那个

def setInterval(sec, func, *args, **kw):
    def inner():
        func(*args, **kw)
        setInterval(sec, func, *args, **kw) # This is where it sends it again
    task = threading.Timer(sec, inner)
    task.daemon = True
    task.start()
    return task

正如您所看到的,它可以工作,但是我没有办法取消线程,因为原始版本再次执行并创建一个新版本才能取消它。如何设置它,如果线程被取消,它将不会创建相同原始线程的任何副本?我已经尝试添加一些关键字,然后在其中发送线程,如果它是一种threading.Timer,它取消它,但它似乎没有工作,因为它已经取消原来的副本,它可以取消,任何想法或建议?我只是想想一种方式,所以它知道它是原件的副本然后不执行它但我不确定我将如何实际做到这一点。有什么我可以做的,所以它终止/取消原始线程,并且在它有机会被取消之前不会启动它的新副本吗?这是我尝试做的事情,因为你想告诉我我做错了什么。

def setInterval(sec = None, func = None, *args, **kw):
    start = True if type(func) != threading.Timer else func.cancel() # doesn't work anyways
    def inner():
        func(*args, **kw)
        setInterval(sec, func, *args, **kw)
    if start == True: 
        task = thrading.Timer(sec, inner)
        task.daemon = True
        task.start()
        return task
def clearInterval(task):
    setInterval(func = task)
myInterval = setInterval(10, print, "Hello, world!")
clearInterval(myInterval) # cancels original, continues to make copies 

1 个答案:

答案 0 :(得分:1)

在运行前取消Timer会阻止它调用其回调函数。但如果它已经被唤醒,取消它已经太晚了。而且,由于您的代码在回调中创建了 new Timer,因此新的计时器不会知道它应该被取消。

因此,你需要配合回调函数,让它访问一些标志,让它知道它被取消了(例如,使用可变的全局或闭包变量,函数属性或默认参数值) )。当然,这个变量需要同步,这意味着你在Lock下阅读它,或者你有竞争条件。 (在CPython中,由于GIL,该种族的最坏情况应偶尔运行一次,但在不同的实现中,计时器线程(回调运行的地方)可能永远不会看到更新的值。)


然而,这将变得复杂。您可能最好先将Timer课程扩展为RepeatingTimer。然后,如果你真的想要,你可以用简单的setInterval / clearInterval函数包装它。

ActiveState上的大量配方和PyPI上的包添加了repeat标志或等价物,还做了其他很好的事情,比如使用单个线程而不是为每个间隔创建一个新线程。但如果你想知道如何自己做,这很容易。事实上,threading docsthreading.py source的链接,因为它可以作为示例代码使用,你可以看到琐碎的Timer是多么的,所以你甚至可以自己重新实现它。


但是,让我们用子类来做。

class RepeatableTimer(threading.Timer):
    def __init__(self, interval, 
                 function, args=None, kwargs=None, repeat=False):
        super(RepeatableTimer, self).__init__(interval, function, args, kwargs)
        self.repeat = repeat
        self.lock = threading.Lock()
    def cancel(self):
        with self.lock:
            self.repeat = False
        super(RepeatableTimer, self).cancel()
    def run(self):
        while True:
            self.finished.clear()
            super(RepeatableTimer, self).run()
            with self.lock:
                if not self.repeat:
                    break

我认为从头开始实施它实际上会更简单,因为您不必担心重置Event,但无论如何,这都有效。


如果您想延长此项,而不是repeat标志,那么times整数(-1为"永远重复&#34 ;)在JavaScript中,这是微不足道的。


无论如何,现在您可以将其包含在setIntervalclearInterval函数中:

def setInterval(sec=None, func=None, *args, **kw):
    task = RepeatableTimer(sec, func, args, kw, repeat=True)
    task.daemon = True
    task.start()
    return task

def clearInterval(task):
    task.cancel()

虽然注意到clearInterval = RepeatableTimer.cancel也会起作用。 (在Python 2.x中,这将是一个未绑定的方法与一个函数,但它仍然可以工作相同,除了提供不同的错误消息,如果你用错误的args调用它。在3.x,没有区别完全没有。)

如果您真的希望通过拨打clearIntervalsetInterval来完成整个过程,那么您可以,但至少要清理一下使用isinstance而不是type的{​​{1}},并且当你可以在一个if中完成所有操作时,不要尝试设置你在第二个if中使用的标记:

def setInterval(sec=None, func=None, *args, **kw):
    if isinstance(func, RepeatableTimer):
        task.cancel()
    else:
        task = RepeatableTimer(sec, func, args, kw, repeat=True)
        task.daemon = True
        task.start()
        return task

def clearInterval(task):
    setInterval(func=task)

但我不知道你认为那会给你买什么。


无论如何,这是验证其有效的测试:

myInterval = setInterval(1, print, "Hello, world!")
time.sleep(3)
clearInterval(myInterval)
time.sleep(5)

这应该通常打印"你好,世界!" 2或3次,偶尔4次,但从不7或8次像一个不可取消的计时器。