你知道如何使用Python实现定时任务吗?threading.Timer()就像是一个方便的定时器工具,能帮我们轻松搞定这件事。今天,咱们就来深入了解一下如何用它实现定时任务,以及在单线程和多线程场景下的具体表现。

一、threading.Timer()基础认知

threading.Timer()本质上就是一个定时器,利用它可以启动多个定时任务。这些任务有个特点,它们是异步执行的,也就是说,各个任务之间不会相互等待,而是各自按照设定的时间独立运行。这在很多场景下都非常实用,比如定时检查系统状态、定时备份数据等。

threading.Timer()提供了几个常用方法,下面给大家详细介绍:

  • Timer(interval, function, args=None, kwargs=None):这是用来创建定时器的方法。interval表示任务执行的间隔时间,单位是秒;function是要执行的任务函数;argskwargs是可选参数,用于给任务函数传递参数。
  • 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了。