如何在 class 中使用线程作为倒数计时器?

How to use threads inside a class that act as a countdown timer?

我想定义一个包含 idstatuscountdown timer 的对象。一旦我实例化这个对象,倒数计时器应该立即开始。

下面是我创建 class 的代码,其中定义了一个非线程版本的倒数计时器

import time
import sys
from threading import Timer

class UserTimer:
    def __init__(self, id=None, current_status=None):
        self.id = id
        self.current_status = current_status
        self.timeout()

    def timeout(self):
        print("timeout started for", self.id)
        timeout_limit = 150
        seconds = 0
        while True:
            try:
                if seconds == timeout_limit:
                    print("countdown over for", self.id)
                    break
                time.sleep(1)
                seconds += 1
            except KeyboardInterrupt, e:
                break

下面是如何实例化它

params1 = {'id': "test@hello.com", 'current_status': "success"}
params2 = {'id': "test2@hello.com", 'current_status': "success"}
user1 = UserTimer(**params1)
user2 = UserTimer(**params2)

这里的问题是当这个程序运行s时,它会实例化第一个对象(user1)并且由于函数time.sleep(),它会等待给定的实例化第二个对象之前的持续时间 (user2)

所以我随后查找并发现 python 线程在这种情况下很有用,因为线程将 运行 独立并且不会阻止代码的执行。

这就是我更改下面代码的方式

class UserTimer:
    def __init__(self, id=None, current_status=None):
        self.id = id
        self.current_status = current_status

    def timeout(self):
        print("time over for", self.id)


    t = Timer(150, timeout)
    t.start()


params1 = {'id': "test@hello.com", 'current_status': "success"}
params2 = {'id': "test2@hello.com", 'current_status': "success"}
user1 = UserTimer(**params1)
user2 = UserTimer(**params2)

现在两个对象都同时实例化了,但问题是一旦给定的持续时间结束,就会出现以下错误

Exception in thread Thread-1:
Traceback (most recent call last):
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/threading.py", line 801, in __bootstrap_inner
    self.run()
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/threading.py", line 1073, in run
    self.function(*self.args, **self.kwargs)
TypeError: timeout() takes exactly 1 argument (0 given)

这是因为它看到了它不期望的关键字 self。但是我需要 self 因为我需要转储一些关于用户对象的信息。如果我删除 self,那么 运行 没问题。

我是否以错误的方式在 class 中定义线程?我想要的是能够实例化多个具有自己的倒数计时器的对象。

此外,我还应该能够通过 user1.reset_timer = True

之类的操作来重置倒数计时器

我做错了什么?

你应该把Timer的初始化放在__init__()里面,在你实例化一个对象的时候启动它。

class UserTimer(object):
    def __init__(self, id=None, current_status=None):
        self.id = id
        self.current_status = current_status
        self.start_timer()   # start timer when an object is instantiated

    def timeout(self):
        print("time over for", self.id)

    def start_timer(self):
        self.t = Timer(5, self.timeout)  # changed to 5 seconds for demo                                                 
                                         # `self.timeout` instead of `timeout`
        self.t.start()

    def reset_timer(self):
        self.t.cancel()      # cancel old timer
        self.start_timer()   # and start a new one

params1 = {'id': "test@hello.com", 'current_status': "success"}
params2 = {'id': "test2@hello.com", 'current_status': "success"}
user1 = UserTimer(**params1)

time.sleep(2)     # assuming that user2 is instantiated 2 seconds later
print('instantiating user2...')
user2 = UserTimer(**params2)

time.sleep(3)     # reset user1's timer 3 seconds after user2 instantiation
print('reseting timer of user1...')
user1.reset_timer()

您可以运行代码并查看时间。

我从你的代码中得到的错误与你的不同:

TypeError: timeout() missing 1 required positional argument: 'self'

线索是提到 self 是由以下原因引起的:

    t = Timer(150, timeout)
    t.start()

你在 UserTimer 的 class 主体中,这意味着它在定义 class 时执行(并且尚未创建 self 实例) .

解决这个问题的一个简单方法是通过定义 __call__() 方法(并在正确的时间调用它)来使 class 的实例可调用。这就是我的意思:

import time
import sys
from threading import Timer


class UserTimer:
    def __init__(self, id=None, current_status=None, interval=5):
        self.id = id
        self.current_status = current_status
        self.interval = interval

    def timeout(self):
        print("time over for", self.id)

    def __call__(self):
        self.timer_thread = Timer(self.interval, self.timeout)
        self.timer_thread.start()

    def cancel(self):
        try:
            self.timer_thread.cancel()
        except AttributeError:
            raise RuntimeError("'UserTimer' object not started.")


params1 = dict(id="test@hello.com", current_status="success")
params2 = dict(id="test2@hello.com", current_status="success", interval=6)
user1 = UserTimer(**params1)
user1()  # Start Timer.
user2 = UserTimer(**params2)
user2()  # Start Timer.

我还按照您的要求添加了 cancel() 方法,并使时间间隔成为一个易于更改的变量,而不是将其也硬编码到 class 的主体中。