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 类代替

主要分成三步

  1. 生成调度器

    s = sched.scheduler(time.time,time.sleep)
    第一个参数是一个可以返回时间戳的函数,第二个参数可以在定时未到达之前阻塞

  2. 加入调度事件

    s.enter(x1,x2,x3,x4)

  3. 运行

    s.run()
    注意 sched 模块不是循环的,一次调度被执行后就 Over 了,如果想再执行,请再次 enter

sched.scheduler(timefunc,delayfunc)

  • timefunc 应该返回一个数字,代表当前时间
  • elayfunc 函数接受一个参数,用于暂停运行的时间单元

一般使用默认参数就行,即传入这两个参数 time.timetime.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不应当是死循环类型的,也就是说,这个线程应该有一个执行完毕的出口

posted @ 2021-11-21 13:32  贝壳里的星海  阅读(760)  评论(0编辑  收藏  举报