如何使用threading.Timer()实现定时任务(含单线程与多线程场景)
你知道如何使用Python实现定时任务吗?threading.Timer()
就像是一个方便的定时器工具,能帮我们轻松搞定这件事。今天,咱们就来深入了解一下如何用它实现定时任务,以及在单线程和多线程场景下的具体表现。
一、threading.Timer()基础认知
threading.Timer()
本质上就是一个定时器,利用它可以启动多个定时任务。这些任务有个特点,它们是异步执行的,也就是说,各个任务之间不会相互等待,而是各自按照设定的时间独立运行。这在很多场景下都非常实用,比如定时检查系统状态、定时备份数据等。
threading.Timer()
提供了几个常用方法,下面给大家详细介绍:
Timer(interval, function, args=None, kwargs=None)
:这是用来创建定时器的方法。interval
表示任务执行的间隔时间,单位是秒;function
是要执行的任务函数;args
和kwargs
是可选参数,用于给任务函数传递参数。cancel()
:这个方法用于取消定时器,如果在定时器启动后,你不想让它继续执行任务了,就可以调用这个方法。start()
:调用该方法后,定时器会以线程的方式开始执行任务。join(self, timeout=None)
:它的作用是等待线程执行结束。timeout
是可选参数,表示等待的最长时间,如果不设置,就会一直等待线程结束。
了解了这些基本概念,下面我们通过实际代码示例,看看在单线程和多线程场景下,threading.Timer()
是如何工作的。
二、单线程执行定时任务
(一)示例代码展示
from datetime import datetime from threading import Timer def task(): now = datetime.now() ts = now.strftime("%Y-%m-%d %H:%M:%S") print(ts) def func(): task() t = Timer(3, func) t.start() func()
在这段代码中:
task
函数的作用是获取当前时间,并将其格式化为“年-月-日 时:分:秒”的形式,然后打印出来。func
函数首先调用task
函数打印当前时间,接着创建一个Timer
对象t
,设置间隔时间为3秒,要执行的任务是再次调用func
函数自身。然后启动这个定时器t
。- 最后调用
func
函数,启动整个定时任务流程。
(二)运行结果分析
运行上述代码后,输出结果类似如下:
2022-10-16 20:36:11 2022-10-16 20:36:14 2022-10-16 20:36:17 2022-10-16 20:36:20 2022-10-16 20:36:23 2022-10-16 20:36:26 2022-10-16 20:36:29 2022-10-16 20:36:32 2022-10-16 20:36:35 2022-10-1620:36:38
可以看到,每隔3秒就会打印一次当前时间,这说明定时任务按照预期在执行。
(三)优缺点探讨
这种单线程执行定时任务的方式有它的优点和缺点。优点是能够实现异步任务,不会阻塞主线程的运行,这意味着在执行定时任务的同时,主线程还可以继续处理其他事务。然而,它也存在一些问题。当运行次数过多时,可能会遇到“Pyinstaller maximum recursion depth exceeded Error Resolution 达到最大递归深度”的报错。这是因为代码中func
函数不断递归调用自身,随着调用次数增加,会达到Python默认的递归深度限制。虽然可以通过修改最大递归深度,比如使用sys.setrecursionlimit(100000000)
来暂时解决这个问题,但当程序运行消耗的CPU资源达到上限时,Python会直接销毁程序,导致整个定时任务无法持续稳定运行。
三、多线程执行定时任务
(一)示例代码展示
from datetime import datetime from threading import Timer import threading def task(): now = datetime.now() ts = now.strftime("%Y-%m-%d %H:%M:%S") print(ts) def func(): task() t = Timer(3, func) t.start() if __name__ == '__main__': for i in range(3): thread = threading.Thread(None, func) thread.start()
这段代码在单线程的基础上做了扩展:
- 首先定义了
task
函数和func
函数,功能和单线程示例中的一样。 - 在
if __name__ == '__main__':
代码块中,通过一个for
循环创建了3个线程,每个线程都执行func
函数。这意味着会同时启动3个定时任务,每个任务都独立运行。
(二)运行结果分析
运行上述多线程代码,输出结果类似如下:
2023-01-12 23:13:02 2023-01-12 23:13:02 2023-01-12 23:13:02 2023-01-12 23:13:052023-01-12 23:13:052023-01-12 23:13:05 2023-01-12 23:13:08 2023-01-12 23:13:082023-01-1223:13:08 2023-01-12 23:13:11 2023-01-12 23:13:11 2023-01-12 23:13:11 2023-01-12 23:13:14 2023-01-12 23:13:14 2023-01-12 23:13:14 2023-01-12 23:13:17 2023-01-12 23:13:17
从结果可以看出,在同一时刻,可能会有多个时间被打印出来,这是因为3个线程同时在运行定时任务,每个线程每隔3秒就会执行一次task
函数。
(三)优缺点探讨
多线程执行定时任务的好处是可以同时运行多个定时任务,提高了任务执行的效率。比如在一些需要同时监控多个指标的场景下,就非常适用。不过,多线程也会带来一些问题,比如线程之间的资源竞争、同步问题等,如果处理不当,可能会导致程序出现意想不到的错误。而且,过多的线程会占用大量系统资源,可能会影响系统的整体性能。
通过以上对单线程和多线程使用threading.Timer()
实现定时任务的介绍,相信大家对这个定时器有了更深入的理解。在实际应用中,我们可以根据具体需求选择合适的方式,充分发挥threading.Timer()
的优势,学会这些,是不是感觉以后搞定时任务更easy了。