起因#
最近在写自动签到脚本,想挂在服务器上每天 12 点执行,由此牵扯出一大堆类似的定时问题
本篇解答一下
每隔 xx 秒执行一次程序#
这是基础,用time.sleep
即可做到,直接上程序:
# 每5秒打印一次
from datetime import datetime
from time import sleep
def print_message():
print(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
print("https://soapffz.com")
while True:
print_message()
sleep(5)
由此可以尝试我们的目标:
from datetime import datetime
from time import sleep
def print_message():
print(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
print("https://soapffz.com")
while True:
while True:
now = datetime.now()
if now.hour == 0 and now.minute == 0:
break
sleep(20)
print_message()
虽然简单粗暴,但是这个while
循环将会一直占用CPU
资源,一般不建议使用
threading.Timer 定时器#
上面一种太过于暴力,我们换一种优雅的方法
threading
中有个Timer
类。它会新启动一个线程来执行定时任务,所以它是非阻塞函式。
如果你有使用多线程的话,需要关心线程安全问题。那么你可以选使用 threading.Timer 模块。
Timer()
函数可接受三个参数:
Timer(10, task, ()).start()
延迟多长时间执行任务(单位: 秒)
要执行的任务, 即函数
调用函数的参数(tuple)
延迟 xx 秒执行程序,只一次#
from datetime import datetime
from time import sleep
from threading import Timer
def print_message():
print(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
print("https://soapffz.com")
Timer(10, print_message).start()
每隔 xx 秒执行一次程序,一直执行#
其实就是把Timer()
语句移到执行的函数里
from datetime import datetime
from time import sleep
from threading import Timer
def print_message():
Timer(10, print_message).start()
print(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
print("https://soapffz.com")
print_message()
同理,需要每隔 24 小时 (不论起始时间),只需要把Timer
函数中的时间改为 86400 即可
schedule 模块定时执行任务#
官方文档:https://schedule.readthedocs.io/en/stable/
这是一个轻量级的定时任务调度的库,我们来看几个例子:
from datetime import datetime
from time import sleep
import schedule
def print_message():
print(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
print("https://soapffz.com")
schedule.every(15).seconds.do(print_message)
schedule.every().minutes.do(print_message)
schedule.every().hour.do(print_message)
schedule.every().day.at("10:30").do(print_message)
schedule.every(5).to(10).days.do(print_message)
schedule.every().wednesday.at("13:15").do(print_message)
while True:
schedule.run_pending()
sleep(1)
上面的意思就是:
每隔15秒执行一次任务
每隔1分钟执行一次任务
每隔1小时执行一次任务
每天的10:30执行一次任务
每隔5到10天执行一次任务
每周一的这个时候执行一次任务
每周三13:15执行一次任务
run_pending:运行所有可以运行的任务
注意到运行结果中有一次重复打印了,不知道什么原因~可以在留言区探讨
schedule.run_pending () 是个什么东西呢 ——
schedule 其实就只是个定时器。在 while True 死循环中,schedule.run_pending () 是保持 schedule 一直运行,去查询上面那一堆的任务,在任务中,就可以设置不同的时间去运行。跟 linux 中设置 crontab 定时任务是类似的。
所以,schedule 有一定的局限性,所以只能用来执行一些小型的定时任务,它的局限性在哪呢 ——
1. 需要定时运行的函数 job 不应当是死循环类型的,也就是说,这个线程应该有一个执行完毕的出口。一是因为线程万一僵死,会是非常棘手的问题;二是下一次定时任务还会开启一个新的线程,执行次数多了就会演变成灾难。
2. 如果 schedule 的时间间隔设置得比 job 执行的时间短,一样会线程堆积形成灾难,也就是说,我 job 的执行时间是 1 个小时,但是我定时任务设置的是 5 分钟一次,那就会一直堆积线程。
另外还有一个库sched.scheduler
,用法和Timer()
差不多,有兴趣的可以自己了解一下
总结#
最后,我使用了datetime
+sleep
搭配的第一种方法和schedule
的第三种方法做对比
服务器配置:1 核心 CPU,1838MB 内存
第一种在平时的消耗如下:
第三种在平时的消耗如下:
虽然看起来后面一种好像平时更占优势,但是当时间到达指定时间的时候
前者很稳定,也不占什么 CPU,后者不知道发什么疯,占了所有的内存和 CPU,给我推送了超多消息
但是我看了下程序也没问题,所以我个人还是建议使用前者,稍微稳妥一点,双重循环还是很致命的。
就酱,本文完。
参考文章: