Python系列之循环定时器

本文介绍如何使用Python的threading模块创建一次性及循环定时任务。通过继承_Thread类并重写run方法,实现了自定义的循环定时器。文章还提供了一个简单的测试示例。

近期在学习并使用Python开发一些小工具,在这里记录方便回忆,也与各位开始走上这条路的朋友共勉,如有不正确希望指正,谢谢!


开始使用定时器时,度娘了下有没好的例子,本人比较懒,希望能直接使用。确实找到了一些,但是大多只是很直白的代码,自己打算整理一下。

我选用了threading模块中的定时器,使用线程的优势就是可以不干扰现有进程的正常执行。首先我们看下源码:

很简单的封装加上对线程的继承,函数也就是运行和取消,并有案例说明

# The timer class was contributed by Itamar Shtull-Trauring

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()

在run函数中 我们不免可以看出,当启动定时器后,一直在等待,然后进行判断set状态,是否执行用户函数,那么所谓的cancel也不是立即取消,而是简单的置状态。判断后也是运行了与cancel一样的代码。所以我们看出这里的定时器是一次性的定时器,而我们需要循环定时器。


现有使用这种定时器进行循环运行的思路是启用2个定时器,进行相互调用。但是是不是逻辑和使用太复杂呢?

那么我们使用更简单的,直接继承timer修改下run函数即可:

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


            t = LoopTimer(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={}):
        _Timer.__init__(self,interval, function, args, kwargs)


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

那么我们写了简单的例子测试下

def testlooptimer():  
    print("I am loop timer.")     
      
t = LoopTimer(2,testlooptimer)  
t.start()

运行结果:

I am loop timer.
I am loop timer.
I am loop timer.
I am loop timer.


<think>我们使用`threading.Timer`来实现定时循环执行函数。根据引用[1]和引用[2]中的示例,我们可以通过在一个函数执行结束时再次设置定时器来实现循环。 具体步骤: 1. 定义一个需要重复执行的函数。 2. 在该函数内部,在函数执行完毕前,重新设置一个定时器定时器到期时再次调用该函数。 3. 启动第一个定时器。 注意:为了能够停止定时器,我们通常需要保留定时器对象的引用,以便在需要时取消。 下面是一个示例代码,每5秒执行一次函数: ```python import threading import time def job(): print(f"定时任务执行: {time.strftime('%H:%M:%S')}") # 重新设置定时器,实现循环 # 注意:这里使用全局变量timer,以便在需要时取消 global timer timer = threading.Timer(5.0, job) # 5秒后再次执行 timer.start() # 初始化定时器 timer = threading.Timer(5.0, job) # 第一个任务在5秒后执行 timer.start() # 主线程可以继续执行其他任务 # 假设程序运行一段时间后停止,比如60秒后停止定时器 time.sleep(60) timer.cancel() # 取消定时器 print("定时任务已停止") ``` 另一种方式是将定时器封装在类中,如引用[2]所示: ```python import threading import time class RepeatingTimer: def __init__(self, interval, function): self.interval = interval self.function = function self.timer = None self.is_running = False def start(self): if not self.is_running: self.is_running = True self._run() def _run(self): if self.is_running: self.function() self.timer = threading.Timer(self.interval, self._run) self.timer.start() def stop(self): if self.timer is not None: self.timer.cancel() self.is_running = False # 使用示例 def job(): print(f"定时任务执行: {time.strftime('%H:%M:%S')}") # 创建一个每5秒执行一次的定时器 timer = RepeatingTimer(5, job) timer.start() # 主线程等待一段时间 time.sleep(60) timer.stop() print("定时任务已停止") ``` 这样封装后,可以更方便地控制定时器的启动和停止。 另外,根据引用[3],如果我们想要在每天的特定时间执行(比如凌晨3点),可以在函数内部计算下一次执行的时间间隔,然后设置定时器。但是,由于用户的问题是关于定时循环执行,所以我们先提供上述两种方法。 如果用户需要更复杂的调度(如每天固定时间),我们可以使用APScheduler等库,但根据用户要求,我们使用threading.Timer来实现。 注意:使用递归调用(在函数内部再次设置定时器)的方式,可能会因为函数执行时间的不确定性导致时间间隔的微小偏差。如果对时间精度要求很高,需要考虑函数执行时间的影响,或者使用系统时间进行校准。 下面是一个每天凌晨3点执行的例子(参考引用[3]): ```python import threading import datetime def job(): print("每天凌晨3点执行的任务") # 计算下一个凌晨3点的时间 now = datetime.datetime.now() next_time = now + datetime.timedelta(days=1) next_time = next_time.replace(hour=3, minute=0, second=0, microsecond=0) # 如果当前时间已经过了今天的3点,则计算明天的3点 if now.hour >= 3: next_time = now.replace(hour=3, minute=0, second=0, microsecond=0) + datetime.timedelta(days=1) # 计算时间差(秒) delta_seconds = (next_time - now).total_seconds() # 设置下一次的定时器 global timer timer = threading.Timer(delta_seconds, job) timer.start() # 计算第一次执行的时间 now = datetime.datetime.now() next_time = now.replace(hour=3, minute=0, second=0, microsecond=0) if now.hour >= 3: next_time += datetime.timedelta(days=1) delta_seconds = (next_time - now).total_seconds() # 启动定时器 timer = threading.Timer(delta_seconds, job) timer.start() ``` 以上代码实现了每天凌晨3点执行一次任务。注意,这里每次执行完任务后,会重新计算下一次执行的时间(即第二天的凌晨3点),然后设置定时器。 综上所述,根据用户的需求(定时循环执行函数),我们可以选择使用简单的循环定时(固定间隔)或者固定时间点执行。 如果用户需要更精确的调度,或者有多个任务,建议使用APScheduler等专业的调度库,但使用threading.Timer可以满足基本的定时循环需求。 请根据实际情况选择合适的方法。</think>在Python中启动定时器循环执行函数,可以使用`threading.Timer`类实现非阻塞的定时循环执行。以下是详细的实现方法和原理: ### 实现原理 `threading.Timer`的工作原理是创建一个延时执行的线程[^1]: 1. 创建一个`Timer`对象,指定延时时间和要执行的函数 2. 调用`start()`启动定时器 3. 定时器在指定时间后在新线程中执行目标函数 4. 在目标函数内部再次创建新的`Timer`对象,实现循环执行 ### 基础实现代码 ```python import threading import time import datetime def job(): """要循环执行的函数""" print(f"任务执行时间: {datetime.datetime.now().strftime('%H:%M:%S')}") # 递归创建新定时器实现循环 threading.Timer(5.0, job).start() # 5秒后再次执行 # 首次启动定时器(5秒后执行) threading.Timer(5.0, job).start() print("主线程继续运行...") # 主线程可以继续执行其他任务 time.sleep(30) # 主线程运行30秒 ``` ### 封装为可管理类 ```python import threading import datetime class RepeatingTimer: """可管理的循环定时器""" def __init__(self, interval, function): """ 初始化定时器 :param interval: 执行间隔(秒) :param function: 要执行的函数 """ self.interval = interval self.function = function self.timer = None self.is_running = False def start(self): """启动定时器""" if not self.is_running: self.is_running = True self._schedule_next() def stop(self): """停止定时器""" if self.timer: self.timer.cancel() self.is_running = False def _schedule_next(self): """安排下一次执行""" if self.is_running: self.timer = threading.Timer(self.interval, self._execute) self.timer.start() def _execute(self): """执行目标函数并安排下一次""" self.function() self._schedule_next() # 使用示例 def my_job(): print(f"定时任务执行: {datetime.datetime.now().strftime('%H:%M:%S')}") # 创建每3秒执行一次的定时器 timer = RepeatingTimer(3.0, my_job) timer.start() # 主线程可以继续工作... input("按Enter键停止定时器...\n") timer.stop() ``` ### 关键特性 1. **非阻塞**:定时任务在独立线程执行,不阻塞主线程[^1] 2. **精确控制**:可随时启动/停止定时器 3. **递归实现**:每次执行后重新创建定时器实现循环 4. **异常处理**:建议在目标函数中添加异常处理 ### 特定时间执行(如每天凌晨3点) ```python import threading import datetime def daily_job(): """每天凌晨3点执行的任务""" print("执行每日任务...") # 计算明天凌晨3点 now = datetime.datetime.now() next_time = now.replace(hour=3, minute=0, second=0, microsecond=0) if now >= next_time: next_time += datetime.timedelta(days=1) # 计算时间差(秒) delta = (next_time - now).total_seconds() # 设置下次定时器 threading.Timer(delta, daily_job).start() # 首次启动 daily_job() ``` ### 注意事项 1. **时间精度**:实际执行时间可能有微小偏差(毫秒级) 2. **资源管理**:长时间运行需注意内存泄漏问题 3. **异常处理**:目标函数应包含异常处理,避免定时中断 4. **线程安全**:如果操作共享资源,需要添加线程同步 对于更复杂的调度需求(如持久化任务、分布式调度),建议使用APScheduler等专业库[^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值