Python threading Timer 在内部是如何工作的?

How Python threading Timer work internally?

我想知道 python threading.Timer 是如何工作的。

更详细地说,当我 运行 几个 threading.Timer 时,它是否 运行 单独的线程来计算时间和 运行 处理程序?

或者一个线程一起管理和计数几个定时器?

我问是因为我的应用程序需要安排很多事件,但是

如果threading.Timer 运行将每个线程分开来计数一个定时器,并且我运行个定时器,它可能会非常影响性能。

所以我担心如果我必须实现一个调度程序 运行如果它对性能有很大影响,那么只需要一个线程。

threading.Timer class 是 threading.Thread 的子 class 并且基本上它只是运行一个单独的线程,它在指定的时间内休眠并运行对应函数.

这绝对不是安排活动的有效方式。更好的方法是使用 Queue.PriorityQueue 在单个线程中进行调度,您可以将事件放在 "priority" 实际上意味着 "next fire date" 的位置。类似于 cron 的工作方式。

甚至更好:使用已经存在的东西,不要重新发明轮子:Cron、Celery 等等...

通过Queue.PriorityQueue制作调度器的一个非常简单的例子:

import time
from Queue import PriorityQueue

class Task(object):
    def __init__(self, fn, crontab):
        # TODO: it should be possible to pass args, kwargs
        # so that fn can be called with fn(*args, **kwargs)
        self.fn = fn
        self.crontab = crontab

    def get_next_fire_date(self):
        # TODO: evaluate next fire date based on self.crontab
        pass

class Scheduler(object):
    def __init__(self):
        self.event_queue = PriorityQueue()
        self.new_task = False

    def schedule_task(self, fn, crontab):
        # TODO: add scheduling language, crontab or something
        task = Task(fn, crontab)
        next_fire = task.get_next_fire_date()
        if next_fire:
            self.new_task = True
            self.event_queue.put((next_fire, task))

    def run(self):
        self.new_task = False

        # TODO: do we really want an infinite loop?
        while True:
            # TODO: actually we want .get() with timeout and to handle
            # the case when the queue is empty
            next_fire, task = self.event_queue.get()

            # incremental sleep so that we can check
            # if new tasks arrived in the meantime
            sleep_for = int(next_fire - time.time())
            for _ in xrange(sleep_for):
                time.sleep(1)
                if self.new_task:
                    self.new_task = False
                    self.event_queue.put((next_fire, task))
                    continue

            # TODO: run in separate thread?
            task.fn()

            time.sleep(1)
            next_fire = task.get_next_fire_date()

            if next_fire:
                event_queue.put((next_fire, task))

def test():
    return 'hello world'

sch = Scheduler()
sch.schedule_task(test, '5 * * * *')
sch.schedule_task(test, '0 22 * * 1-5')
sch.schedule_task(test, '1 1 * * *')
sch.run()

这只是一个想法。您必须正确实施 TaskScheduler classes,即 get_next_fire_date 方法加上某种调度语言(crontab?)和错误处理。我仍然强烈建议使用现有的库之一。

来自 CPython 2.7 源代码:

def Timer(*args, **kwargs):
    """Factory function to create a Timer object.

    Timers call a function after a specified number of seconds:

        t = Timer(30.0, f, args=[], kwargs={})
        t.start()
        t.cancel()     # stop the timer's action if it's still waiting

    """
    return _Timer(*args, **kwargs)

class _Timer(Thread):
    """Call a function after a specified number of seconds:

            t = Timer(30.0, f, args=[], kwargs={})
            t.start()
            t.cancel()     # stop the timer's action if it's still waiting

    """

    def __init__(self, interval, function, args=[], kwargs={}):
        Thread.__init__(self)
        self.interval = interval
        self.function = function
        self.args = args
        self.kwargs = kwargs
        self.finished = Event()

    def cancel(self):
        """Stop the timer if it hasn't finished yet"""
        self.finished.set()

    def run(self):
        self.finished.wait(self.interval)
        if not self.finished.is_set():
            self.function(*self.args, **self.kwargs)
        self.finished.set()

如另一个答案所述,它是一个单独的线程(因为它是 Thread 的子类)。定时器超时时的回调函数从新线程调用。