每隔N秒在后台线程中重复一次函数

时间:2017-11-14 17:14:43

标签: python python-3.x python-multithreading

我知道这听起来很像this similarly-worded question,但也有差异,所以请耐心等待。

我试图创建一个可重复使用的"计时器"每隔N秒调用指定回调的类,直到您调用stop。作为灵感,我使用上面的链接,内置事件包裹在stop方法中。以下是基础课的外观:

import time
import threading
from threading import Thread
from threading import Event

# Mostly inspired by https://stackoverflow.com/questions/12435211/python-threading-timer-repeat-function-every-n-seconds
class RepeatingTimer(Thread):
    def __init__(self, interval_seconds, callback):
        Thread.__init__(self)
        self.stop_event = Event()
        self.interval_seconds = interval_seconds
        self.callback = callback
        self.setDaemon(True)

    def start(self):
        while not self.stop_event.wait(self.interval_seconds):
            self.callback()
            time.sleep(0) # doesn't seem to do anything

    def stop(self):
        self.stop_event.set()

看起来不错,甚至包括基于this questiontime.sleep(0)

它没有做我的想法;对start的呼唤似乎永远不会回归或屈服。考虑一下这个用例:

def print_status(message):
  print(message)

def print_r1():
  print_status("R1")

def print_r2():
  print_status("R2")

r1 = RepeatingTimer(1, print_r1)
r2 = RepeatingTimer(0.5, print_r2)

r1.start()
r2.start()

r1.start的调用永远不会终止。它会永远持续下去。四秒钟后,控制台上的输出为:

  

R1

     

R1

     

R1

     

R1

这促使我介绍了time.sleep(0)电话,虽然这似乎没有做任何事情。

我也尝试过使用和不使用self.setDaemon(True),但这似乎也没有效果。

我也尝试将它转换为两个类:一个只有事件包装器(一个StoppableTimer类),另一个只是在回调中创建并重新创建StoppableTimer,但是没有#39} ;或者工作。这是它的样子:

class StoppableTimer(Thread):
    def __init__(self, interval_seconds, callback):
        Thread.__init__(self)
        self.stop_event = Event()
        self.interval_seconds = interval_seconds
        self.callback = callback
        self.setDaemon(True)

    def start(self):
        time.sleep(self.interval_seconds)
        self.callback()

    def stop(self):
        self.stop_event.set()


class RepeatingTimer:
    def __init__(self, interval_seconds, callback):
        self.interval_seconds = interval_seconds
        self.callback = callback
        self.timer = StoppableTimer(interval_seconds, self.refresh_timer)

    def start(self):
        self.timer.start()

    def stop(self):
        self.timer.stop()

    def refresh_timer(self):
        self.stop()
        self.callback()
        self.timer = StoppableTimer(self.interval_seconds, self.refresh_timer)
        self.timer.start()

我完全不知道如何做这项工作。我也主要是Python的初学者,所以请为你的答案添加足够的解释,以便我掌握基本问题。

我还读了一些Global Interpreter Lock on SO,但我不明白这可能是一个问题。

作为参考,我在Ubuntu 17.10上运行Python 3.6.3

1 个答案:

答案 0 :(得分:3)

简短回答:

不要覆盖start()。改为覆盖run()

答案很长,因为您要求提供详细信息:

通过第一个代码段中的类定义,您创建了一个继承自Thread的类,但是您已经覆盖了start()方法,该方法应该通过在设置stop_event之前循环的新方法,也就是说,实际启动你的线程的方法不再这样做了。

因此,当您尝试启动线程时,实际上是在当前且唯一的线程中运行调用回调函数的循环。因为它是一个无限循环,你的第二个"线程"没有开始,你没有办法停止"停止"它

你不能覆盖start(不是这样)。相反,覆盖run方法。这是您启动它时将由您的线程运行的方法。

此外,您应该super().__init__()而不是Thread.__init__(self)。第一个是在Python中调用继承方法的正确方法。

class RepeatingTimer(Thread):
    def __init__(self, interval_seconds, callback):
        super().__init__()
        self.stop_event = Event()
        self.interval_seconds = interval_seconds
        self.callback = callback

    def run(self):
        while not self.stop_event.wait(self.interval_seconds):
            self.callback()

    def stop(self):
        self.stop_event.set()

使用您已定义的功能,您可以执行以下操作:

r1 = RepeatingTimer(1, print_r1)
r2 = RepeatingTimer(0.5, print_r2)

r1.start()
r2.start()
time.sleep(4)
r1.stop()
r2.stop()

Here is the relevant documentation for Thread