Python threading.timer - 每隔'n'秒重复一次

use*_*282 75 python python-2.7 python-3.x

我对python计时器有困难,非常感谢一些建议或帮助:D

我不太了解线程如何工作,但我只是想每0.5秒触发一次函数,并能够启动和停止并重置计时器.

但是,RuntimeError: threads can only be started once当我执行threading.timer.start()两次时,我会继续得到.有没有解决这个问题?我threading.timer.cancel()在每次开始前尝试申请.

伪代码:

t=threading.timer(0.5,function)
while True:
    t.cancel()
    t.start()
Run Code Online (Sandbox Code Playgroud)

Han*_*hen 95

最好的方法是启动计时器线程一次.在您的计时器线程中,您将编码以下内容

class MyThread(Thread):
    def __init__(self, event):
        Thread.__init__(self)
        self.stopped = event

    def run(self):
        while not self.stopped.wait(0.5):
            print("my thread")
            # call a function
Run Code Online (Sandbox Code Playgroud)

在启动计时器的代码中,您可以使用set已停止的事件来停止计时器.

stopFlag = Event()
thread = MyThread(stopFlag)
thread.start()
# this will stop the timer
stopFlag.set()
Run Code Online (Sandbox Code Playgroud)

  • 好吧,实际上,如果你想立即停止计时器线程,只需使用`threading.Event`和`wait`而不是`sleep`.然后,唤醒它,只需设置事件.你甚至不需要`self.stopped`,因为你只需检查事件标志. (12认同)
  • 然后它将完成它的睡眠并在之后停止.没有办法在python中_forcibly_挂起一个线程.这是python开发人员做出的设计决策.但最终结果将是相同的.你的线程仍会运行(休眠)一段时间,但它不会执行你的功能. (4认同)
  • 该事件将严格用于中断计时器线程.通常情况下,`event.wait`只会超时并且像睡眠一样,但如果你想停止(或以其他方式中断线程),你就会设置线程的事件,它会立即唤醒. (3认同)
  • 我已经更新了我的答案以使用 event.wait()。感谢您的建议。 (2认同)

swa*_*ala 27

使用计时器线程 -

from threading import Timer,Thread,Event


class perpetualTimer():

   def __init__(self,t,hFunction):
      self.t=t
      self.hFunction = hFunction
      self.thread = Timer(self.t,self.handle_function)

   def handle_function(self):
      self.hFunction()
      self.thread = Timer(self.t,self.handle_function)
      self.thread.start()

   def start(self):
      self.thread.start()

   def cancel(self):
      self.thread.cancel()

def printer():
    print 'ipsem lorem'

t = perpetualTimer(5,printer)
t.start()
Run Code Online (Sandbox Code Playgroud)

这可以通过 t.cancel()

  • 我相信这段代码在`cancel`方法中有一个错误.调用此方法时,线程是1)未运行或2)正在运行.在1)我们正在等待运行该功能,所以取消将正常工作.2)我们当前正在运行,因此取消对当前执行没有影响.此外,当前的执行重新安排自己,因此它将来不会产生影响. (3认同)
  • 每次计时器耗尽时,此代码都会创建一个新线程。与公认的答案相比,这是一种巨大的浪费。 (2认同)

jfs*_*jfs 24

来自python中setInterval的等价物:

import threading

def setInterval(interval):
    def decorator(function):
        def wrapper(*args, **kwargs):
            stopped = threading.Event()

            def loop(): # executed in another thread
                while not stopped.wait(interval): # until stopped
                    function(*args, **kwargs)

            t = threading.Thread(target=loop)
            t.daemon = True # stop if the program exits
            t.start()
            return stopped
        return wrapper
    return decorator
Run Code Online (Sandbox Code Playgroud)

用法:

@setInterval(.5)
def function():
    "..."

stop = function() # start timer, the first call is in .5 seconds
stop.set() # stop the loop
stop = function() # start new timer
# ...
stop.set() 
Run Code Online (Sandbox Code Playgroud)

或者这里是相同的功能,但作为独立的功能而不是装饰器:

cancel_future_calls = call_repeatedly(60, print, "Hello, World")
# ...
cancel_future_calls() 
Run Code Online (Sandbox Code Playgroud)

以下是不使用线程的方法.


小智 16

为了按照 OP 的要求使用 Timer 提供正确答案,我将改进swapnil jariwala 的答案

from threading import Timer


class InfiniteTimer():
    """A Timer class that does not stop, unless you want it to."""

    def __init__(self, seconds, target):
        self._should_continue = False
        self.is_running = False
        self.seconds = seconds
        self.target = target
        self.thread = None

    def _handle_target(self):
        self.is_running = True
        self.target()
        self.is_running = False
        self._start_timer()

    def _start_timer(self):
        if self._should_continue: # Code could have been running when cancel was called.
            self.thread = Timer(self.seconds, self._handle_target)
            self.thread.start()

    def start(self):
        if not self._should_continue and not self.is_running:
            self._should_continue = True
            self._start_timer()
        else:
            print("Timer already started or running, please wait if you're restarting.")

    def cancel(self):
        if self.thread is not None:
            self._should_continue = False # Just in case thread is running and cancel fails.
            self.thread.cancel()
        else:
            print("Timer never started or failed to initialize.")


def tick():
    print('ipsem lorem')

# Example Usage
t = InfiniteTimer(0.5, tick)
t.start()
Run Code Online (Sandbox Code Playgroud)


rig*_*cky 12

改善Hans Then的答案,我们可以将Timer函数子类化。以下代码成为我们完整的 “重复计时器”代码,并且可以用作具有所有相同参数的thread.Timer的直接替代:

from threading import Timer

class RepeatTimer(Timer):
    def run(self):
        while not self.finished.wait(self.interval):
            self.function(*self.args, **self.kwargs)
Run Code Online (Sandbox Code Playgroud)

用法示例:

def dummyfn(msg="foo"):
    print(msg)

timer = RepeatTimer(1, dummyfn)
timer.start()
time.sleep(5)
timer.cancel()
Run Code Online (Sandbox Code Playgroud)

产生以下输出:

foo
foo
foo
foo
Run Code Online (Sandbox Code Playgroud)

timer = RepeatTimer(1, dummyfn, args=("bar",))
timer.start()
time.sleep(5)
timer.cancel()
Run Code Online (Sandbox Code Playgroud)

产生

bar
bar
bar
bar
Run Code Online (Sandbox Code Playgroud)

  • 非常优雅的解决方案!奇怪的是他们不只是包含一个执行此操作的类。 (3认同)
  • 这个解决方案非常令人印象深刻,但我很难通过简单地阅读[Python3线程定时器接口文档](https://docs.python.org/3/library/threading.html)来理解它是如何设计的。答案似乎建立在通过进入“threading.py”模块本身来了解实现的基础上。 (3认同)
  • 非常好的解决方案!...如果您只想调用 print,实际上不需要 `dummyfn`...`RepeatTimer(1, print, args=("my message",))` 也能同样出色地完成这项工作! (2认同)

Gio*_* PY 5

我更改了 swapnil-jariwala 代码中的一些代码来制作一个小控制台时钟。

from threading import Timer, Thread, Event
from datetime import datetime

class PT():

    def __init__(self, t, hFunction):
        self.t = t
        self.hFunction = hFunction
        self.thread = Timer(self.t, self.handle_function)

    def handle_function(self):
        self.hFunction()
        self.thread = Timer(self.t, self.handle_function)
        self.thread.start()

    def start(self):
        self.thread.start()

def printer():
    tempo = datetime.today()
    h,m,s = tempo.hour, tempo.minute, tempo.second
    print(f"{h}:{m}:{s}")


t = PT(1, printer)
t.start()
Run Code Online (Sandbox Code Playgroud)

输出

>>> 11:39:11
11:39:12
11:39:13
11:39:14
11:39:15
11:39:16
...
Run Code Online (Sandbox Code Playgroud)

带有 tkinter 图形界面的计时器

这段代码使用 tkinter 将时钟计时器放在一个小窗口中

from threading import Timer, Thread, Event
from datetime import datetime
import tkinter as tk

app = tk.Tk()
lab = tk.Label(app, text="Timer will start in a sec")
lab.pack()


class perpetualTimer():

    def __init__(self, t, hFunction):
        self.t = t
        self.hFunction = hFunction
        self.thread = Timer(self.t, self.handle_function)

    def handle_function(self):
        self.hFunction()
        self.thread = Timer(self.t, self.handle_function)
        self.thread.start()

    def start(self):
        self.thread.start()

    def cancel(self):
        self.thread.cancel()


def printer():
    tempo = datetime.today()
    clock = "{}:{}:{}".format(tempo.hour, tempo.minute, tempo.second)
    try:
        lab['text'] = clock
    except RuntimeError:
        exit()


t = perpetualTimer(1, printer)
t.start()
app.mainloop()
Run Code Online (Sandbox Code Playgroud)

抽认卡游戏的一个例子(有点)

from threading import Timer, Thread, Event
from datetime import datetime


class perpetualTimer():

    def __init__(self, t, hFunction):
        self.t = t
        self.hFunction = hFunction
        self.thread = Timer(self.t, self.handle_function)

    def handle_function(self):
        self.hFunction()
        self.thread = Timer(self.t, self.handle_function)
        self.thread.start()

    def start(self):
        self.thread.start()

    def cancel(self):
        self.thread.cancel()


x = datetime.today()
start = x.second


def printer():
    global questions, counter, start
    x = datetime.today()
    tempo = x.second
    if tempo - 3 > start:
        show_ans()
    #print("\n{}:{}:{}".format(tempo.hour, tempo.minute, tempo.second), end="")
    print()
    print("-" + questions[counter])
    counter += 1
    if counter == len(answers):
        counter = 0


def show_ans():
    global answers, c2
    print("It is {}".format(answers[c2]))
    c2 += 1
    if c2 == len(answers):
        c2 = 0


questions = ["What is the capital of Italy?",
             "What is the capital of France?",
             "What is the capital of England?",
             "What is the capital of Spain?"]

answers = "Rome", "Paris", "London", "Madrid"

counter = 0
c2 = 0
print("Get ready to answer")
t = perpetualTimer(3, printer)
t.start()
Run Code Online (Sandbox Code Playgroud)

输出:

Get ready to answer
>>> 
-What is the capital of Italy?
It is Rome

-What is the capital of France?
It is Paris

-What is the capital of England?
...
Run Code Online (Sandbox Code Playgroud)