Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 每N秒在后台线程中重复一个函数_Python_Python 3.x_Python Multithreading - Fatal编程技术网

Python 每N秒在后台线程中重复一个函数

Python 每N秒在后台线程中重复一个函数,python,python-3.x,python-multithreading,Python,Python 3.x,Python Multithreading,我知道这听起来很像,但有不同之处,所以请容忍我 我试图创建一个可重用的“Timer”类,它每N秒调用一次指定的回调,直到调用stop。作为灵感,我使用了上面的链接,其中包含了一个内置的事件,用stop方法包装。下面是基本类的外观: import time import threading from threading import Thread from threading import Event # Mostly inspired by https://stackoverflow.com/

我知道这听起来很像,但有不同之处,所以请容忍我

我试图创建一个可重用的“Timer”类,它每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()
看起来不错,甚至包括
时间。睡眠(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
,但这也不起作用。下面是它的样子:

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的初学者,所以请对您的答案进行充分的解释,以便我能够理解基本问题是什么

我也读了一些关于这个问题的文章,但我不明白这怎么会成为一个问题

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

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

回答很长,因为您需要了解详细信息:

使用第一个代码段中的类定义,您创建了一个从
线程
继承的类,但是您已经覆盖了
开始()
方法,该方法应该通过一个新方法启动线程,该方法循环直到
停止事件
被设置,也就是说,实际启动线程的方法不再这样做了

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

您不能覆盖
启动
(当然不能以这种方式)。而是重写
run
方法。这是启动线程时线程将运行的方法

此外,您应该执行
super()。\uuuuu init\uuuuu()
而不是
Thread.\uuuu init\uuuuu(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()

.

您似乎已经重写了
线程的
开始方法,可以想象,这不是一个好主意,特别是如果您从未调用实际的线程。start@pvg这可能是我在Python方面的经验不足。我从链接线程复制粘贴代码,并对其进行修改,使事件成为成员变量而不是外部性。我将在链接问题中做一个注释以编辑代码示例。您没有,我选中了:)。您已经重写了代码,使其无法工作。有人会注意到它在原始答案中不起作用,并指出它。@pvg我确实复制粘贴了它,但在某个时候,我将
run
重命名为
start
(没有意识到它的含义)。这也许可以解释为什么会有否决票……避免这一切的一个更明智的方法是“不要从线程继承”。线程和计时器之间没有明显的“是”关系。计时器可能是使用线程实现的,它可能包含线程,但它不是线程。@pvg我没有关于它的完整图片。请随意创建一个新答案,并用一些代码解释/说明您的建议。感谢工作代码和详细解释(+1)。我将拭目以待,看看是否有人可以建议如何在不继承
线程的情况下执行此操作,如果几天内什么都没有发生,我将接受这个答案。@nightblade9我认为我为您编写代码来回答不同的问题不会有多大收获。你应该接受这个答案——它回答了你的问题,而这个问题本身实际上是一个“打字错误或不再重复”类型的问题,不太可能帮助其他人。您应该做的是按照建议阅读关于线程的文档-它立即涵盖了子类化和委托案例,并明确告诉您不要覆盖除
运行
以外的方法。您还可以查看线程教程,例如,它也涵盖了这两种情况。@nightblade9另外,当您熟悉Python时,您可能会对asyncio感兴趣。例如: