python-定时器
python实现定时任务
1、while True: + sleep()
import time
def timer(n):
while True:
print(time.strftime('%Y-%m-%d %X',time.localtime()))
# 此处为要执行的任务
time.sleep(n)
timer(2)
#2021-09-23 18:15:01
#2021-09-23 18:15:03
#2021-09-23 18:15:05
特点:
- 简单,每间隔一段时间执行一次
- 死循环+单线程+阻塞函数,只能是实现同步任务,无法异步
- 针对定时任务需要自己来写定时器
- 不容易控制
2、threading模块中的Timer
Timer是Thread的子类,是一个定时器功能的类,就是几秒钟之后执行某个方法
相比与Thread,它多了一个cancel()方法,能在对象还没执行完成的时候停止这个对象
import threading
timer = threading.Timer(interval, function, args=None, kwargs=None)
参数:
interval
:经过多少秒调用函数,单位秒(不断调用则在目标函数末尾调用该方法)function
:调用的目标函数args=None
:传递到目标函数的位置参数kwargs=None
:传递到目标函数的关键字参数
特点:
-
多少秒后执行
-
多线程,不会阻塞
-
循环定时采用递归方式,不使用
while
-
创建线程
import threading
import time
def test():
print("start test")
print("stop test")
if __name__ == "__main__":
t1 = threading.Timer(2, test)
t1.start()
print("main")
# main
# start test
# stop test
案例1
# 递归创建
from datetime import datetime
from threading import Timer
# 打印时间函数
def printTime(inc):
print(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
t = Timer(inc, printTime, (inc,))
t.start()
# 5s
printTime(5)
#2021-09-23 18:26:31
#2021-09-23 18:26:36
#2021-09-23 18:26:41
#2021-09-23 18:26:46
#2021-09-23 18:26:51
#2021-09-23 18:26:56
#2021-09-23 18:27:01
#....
案例2
# -*- coding:utf-8 -*-
import threading
import time
exec_count = 0
def start():
print("hello world", exec_count)
def heart_beat():
print(time.strftime('%Y-%m-%d %H:%M:%S'))
global exec_count
exec_count += 1
# 执行15次后停止定时器
if exec_count < 15:
# 递归结束条件
start()
threading.Timer(5, heart_beat).start()
if __name__ == '__main__':
heart_beat()
3、使用sched模块
sched模块实现了一个python内置标准库,通用事件调度器,在调度器类使用一个延迟函数等待特定的时间,执行任务。同时支持多线程应用程序,在每个任务执行后会立刻调用延时函数,以确保其他线程也能执行。
多线程场景中,会有线程安全问题,run()
函数会阻塞主线程。官方建议使用 threading.Timer 类代替
主要分成三步
-
生成调度器
s = sched.scheduler(time.time,time.sleep)
第一个参数是一个可以返回时间戳的函数,第二个参数可以在定时未到达之前阻塞 -
加入调度事件
s.enter(x1,x2,x3,x4)
-
运行
s.run()
注意 sched 模块不是循环的,一次调度被执行后就 Over 了,如果想再执行,请再次 enter
sched.scheduler(timefunc,delayfunc)
timefunc
应该返回一个数字,代表当前时间elayfunc
函数接受一个参数,用于暂停运行的时间单元
一般使用默认参数就行,即传入这两个参数 time.time
和 time.sleep
schedule.enter(delay,priority,action,arguments)
-
参数
delay
时间,多少秒后执行 -
参数
priority
优先级,0最高,1次之,2次次之 -
参数
action
事件,要执行的函数 -
参数
arguments
执行函数名函数的参数,最好用括号包起来,如果只传入一个参数的时候用括号包起来,该参数后面一定要加一个逗号
scheduler.run()
一直被阻塞,直到所有任务全部执行结束,每个任务在同一线程中运行,所以如果一个任务执行时间大于其他任务的等待时间,那么其他任务会推迟任务的执行时间,这样保证没有任务丢失,但这些任务的调用时间会比设定的推迟。
scheduler 中的每个调度任务只会工作一次,不会无限循环被调用
案例1
import sched
import time
#生成调度器
scheduler = sched.scheduler(time.time, time.sleep)
def print_event(name):
print ('EVENT:', time.time(), name)
print ('START:', time.time())
#分别设置在执行后2秒、3秒之后执行调用函数
scheduler.enter(2, 1, print_event, ('first',))
scheduler.enter(3, 1, print_event, ('second',))
#运行调度器
scheduler.run()
案例2
# 定时执行任务
import time,os,sched
schedule = sched.scheduler(time.time,time.sleep)
def perform_command(cmd,inc):
os.system(cmd)
print('task')
def timming_exe(cmd,inc=60):
schedule.enter(inc,0,perform_command,(cmd,inc))
schedule.run()
print('show time after 2 seconds:')
timming_exe('echo %time%',2)
# 周期性执行任务
schedule = sched.scheduler(time.time,time.sleep)
def perform_command(cmd,inc):
#在inc秒后再次运行自己,即周期运行
os.system(cmd)
schedule.enter(inc, 0, perform_command, (cmd, inc))
def timming_exe(cmd,inc=60):
schedule.enter(inc,0,perform_command,(cmd,inc))
schedule.run()#持续运行,直到计划时间队列变成空为止
print('show time after 2 seconds:')
timming_exe('echo %time%',2)
4、使用schedule库
python中有一个第三方轻量级的任务调度模块:schedule
。可以按照秒,分,小时,日期或者自定义事件执行时间。因此十分方便我们执行一些轻量级的定时任务
参考文献:https://blog.csdn.net/chen801090/article/details/93335733
参考文献:https://schedule.readthedocs.io
- schedule 优点是简单、轻量级、无需配置、语法简单,
- 缺点是阻塞式调用、无法动态添加或删除任务
安装:
pip install schedule
官方案例
import schedule
import time
# 定义你要周期运行的函数
def job():
print("I'm working...")
schedule.every(10).minutes.do(job) # 每隔 10 分钟运行一次 job 函数
schedule.every().hour.do(job) # 每隔 1 小时运行一次 job 函数
schedule.every().day.at("10:30").do(job) # 每天在 10:30 时间点运行 job 函数
schedule.every().monday.do(job) # 每周一 运行一次 job 函数
schedule.every().wednesday.at("13:15").do(job) # 每周三 13:15 时间点运行 job 函数
schedule.every().minute.at(":17").do(job) # 每分钟的 17 秒时间点运行 job 函数
while True:
schedule.run_pending() # 运行所有可以运行的任务
time.sleep(1)
#周一到周日
monday
tuesday
wednesday
thursday
friday
saturday
sunday
#at一些技巧
at(HH:MM:SS)
at("00:00") 凌晨一点
every().hour.at(':30') 每小时30分
every().minute.at(':30') 每分钟30秒
取消任务
schedule.clear()
取消指定任务
#需要引入tap
def greet(name):
print('Hello {}'.format(name))
schedule.every().day.do(greet, 'Andrea').tag('daily-tasks', 'friend')
schedule.every().hour.do(greet, 'John').tag('hourly-tasks', 'friend')
schedule.every().hour.do(greet, 'Monica').tag('hourly-tasks', 'customer')
schedule.every().day.do(greet, 'Derek').tag('daily-tasks', 'guest')
schedule.clear('daily-tasks') # q取消所有标签为daily-tasks的任务
装饰器
import time
from schedule import every, repeat, run_pending
@repeat(every().second)
def job():
print('working...')
while True:
run_pending()
time.sleep(1)
马上运行任务(用于测试)
import schedule
def job():
print('working...')
def job1():
print('Hello...')
schedule.every().monday.at('12:40').do(job)
schedule.every().tuesday.at('16:40').do(job1)
schedule.run_all()
schedule.run_all(delay_seconds=3) # 任务间延迟3秒
案例
import datetime
import schedule
import time
def func():
now = datetime.datetime.now()
ts = now.strftime('%Y-%m-%d %H:%M:%S')
print('do func time :',ts)
def func2():
now = datetime.datetime.now()
ts = now.strftime('%Y-%m-%d %H:%M:%S')
print('do func2 time:',ts)
def tasklist():
#清空任务
schedule.clear()
#创建一个按秒间隔执行任务
schedule.every(1).seconds.do(func)
#创建一个按2秒间隔执行任务
schedule.every(2).seconds.do(func2)
#执行10S
for i in range(10):
schedule.run_pending()
time.sleep(1)
tasklist()
多任务串行执行任务的延迟问题
import datetime
import schedule
import time
def job1():
print("I'm working for job1")
time.sleep(1)
print("job1:", datetime.datetime.now())
def job2():
print("I'm working for job2")
time.sleep(2)
print("job2:", datetime.datetime.now())
def run():
schedule.every(2).seconds.do(job1)
schedule.every(2).seconds.do(job2)
while True:
schedule.run_pending()
time.sleep(1)
run()
# I'm working for job1
# job1: 2021-09-23 19:11:16.034990
# I'm working for job2
# job2: 2021-09-23 19:11:18.036131
# I'm working for job1
# job1: 2021-09-23 19:11:20.046643
# I'm working for job2
# job2: 2021-09-23 19:11:23.050675
# I'm working for job1
# job1: 2021-09-23 19:11:25.050984
# I'm working for job2
# job2: 2021-09-23 19:11:28.074148
两个定时任务并不是2秒运行一次,而是3秒。是的。由于job1和job2本身的执行时间,导致任务延迟了。
多线程解决延迟问题
import datetime
import schedule
import threading
import time
def job1():
print("I'm working for job1")
time.sleep(2)
print("job1:", datetime.datetime.now())
def job2():
print("I'm working for job2")
time.sleep(2)
print("job2:", datetime.datetime.now())
def job1_task():
threading.Thread(target=job1).start()
def job2_task():
threading.Thread(target=job2).start()
def run():
schedule.every(10).seconds.do(job1_task)
schedule.every(10).seconds.do(job2_task)
while True:
schedule.run_pending()
time.sleep(1)
# 唯一要注意的是,这里面job不应当是死循环类型的,也就是说,这个线程应该有一个执行完毕的出口