Python快速实现定时器的五种常见方法详解

作者:

文章目录
  • 通过 while + sleep 实现定时任务 (1) 存在时间漂移(等待运行时间) import time def loopMonitor(): while True: MonitorSystem() # 1min检查一次 time.sleep(60) loopMonitor() (2) 维护sleep时间 import time def loopMonitor(): while True: MonitorSystem() #2s检查一次 time.sleep(60.0 – ((time.time() – starttime) % 60.0)) loopMonitor()
  • import threading import time class RepeatedTimer(object): def __init__(self, interval, function, *args, **kwargs): self._timer = None self.interval = interval self.function = function self.args = args self.kwargs = kwargs self.is_running = False self.next_call = time.time() self.start() def _run(self): self.is_running = False self.start() self.function(*self.args, **self.kwargs) def start(self): if not self.is_running: self.next_call += self.interval self._timer = threading.Timer(self.next_call – time.time(), self._run) self._timer.start() self.is_running = True def stop(self): self._timer.cancel() self.is_running = False from time import sleep def hello(name): print “Hello %s!” % name print “starting…” rt = RepeatedTimer(1, hello, “World”) # it auto-starts, no need of rt.start() try: sleep(5) # your long-running job goes here… finally: rt.stop() # better in a try/finally block to make sure the program ends!
  • 更加健壮,并且实现了许多功能:守护进程、日志记录或异常处理 from twisted.internet import task, reactor timeout = 60.0 # Sixty seconds def doWork(): #do work here pass l = task.LoopingCall(doWork) l.start(timeout) # call every sixty seconds reactor.run()
  • import schedule import time def job(): print(“I’m working…”) #schedule.every(1)创建Job, seconds.do(func)按秒间隔查询并执行 schedule.every(1).seconds.do(func) #添加任务按分执行 schedule.every(1).minutes.do(func) #添加任务按天执行 schedule.every(1).days.do(func) #添加任务按周执行 schedule.every().weeks.do(func) #添加任务每周一执行,执行时间为下周一这一时刻时间 schedule.every().monday.do(func) schedule.every().monday.at(“12:00”).do(job) # 每隔10分钟执行一次任务 schedule.every(10).minutes.do(job) # 每隔一小时执行一次任务 schedule.every().hour.do(job) # 每天10:30执行一次任务 schedule.every().day.at(“10:30”).do(job) # 每隔5到10分钟运行一次任务? schedule.every(5).to(10).minutes.do(job) # 每周一的这个时候执行一次任务 schedule.every().monday.do(job) # 每周三 13:15执行一次任务 schedule.every().wednesday.at(“13:15”).do(job) # # 每分钟的第17秒执行任务 schedule.every().minute.at(“:17”).do(job) while True: schedule.run_pending() # 检测是否有到期任务 time.sleep(1) # 避免一直查询cpu过高。sleep时间不应该小于执行时间,否则会线程堆积。
  • 目录
    • 1.sleep法(阻塞)
    • 2.自定义法 (+线程、非阻塞)
    • 3.Twisted
    • 4.schedule (简单方便,推荐)
    • 5.Apscheduler (功能多,推荐)
      • 调度器(scheduler)
      • 触发器(trigger)
      • 作业存储(job store)
      • 注意点

    通过 while + sleep 实现定时任务

    (1) 存在时间漂移(等待运行时间)

    import time
    def loopMonitor():
        while True:
            MonitorSystem()
            # 1min检查一次
            time.sleep(60)
    loopMonitor()

    (2) 维护sleep时间

    import time
    def loopMonitor():
        while True:
            MonitorSystem()
            #2s检查一次
            time.sleep(60.0 - ((time.time() - starttime) % 60.0))
    loopMonitor()

    import threading 
    import time
     
    class RepeatedTimer(object):
      def __init__(self, interval, function, *args, **kwargs):
        self._timer = None
        self.interval = interval
        self.function = function
        self.args = args
        self.kwargs = kwargs
        self.is_running = False
        self.next_call = time.time()
        self.start()
     
      def _run(self):
        self.is_running = False
        self.start()
        self.function(*self.args, **self.kwargs)
     
      def start(self):
        if not self.is_running:
          self.next_call += self.interval
          self._timer = threading.Timer(self.next_call - time.time(), self._run)
          self._timer.start()
          self.is_running = True
     
      def stop(self):
        self._timer.cancel()
        self.is_running = False
        
        
    from time import sleep
    def hello(name):
        print "Hello %s!" % name
     
    print "starting..."
    rt = RepeatedTimer(1, hello, "World") # it auto-starts, no need of rt.start()
    try:
        sleep(5) # your long-running job goes here...
    finally:
        rt.stop() # better in a try/finally block to make sure the program ends!

    更加健壮,并且实现了许多功能:守护进程、日志记录或异常处理

    from twisted.internet import task, reactor
     
    timeout = 60.0 # Sixty seconds
     
    def doWork():
        #do work here
        pass
     
    l = task.LoopingCall(doWork)
    l.start(timeout) # call every sixty seconds
     
    reactor.run()

    import schedule
    import time
     
    def job():
        print("I'm working...")
     
    #schedule.every(1)创建Job, seconds.do(func)按秒间隔查询并执行
    schedule.every(1).seconds.do(func)
    #添加任务按分执行
    schedule.every(1).minutes.do(func)
    #添加任务按天执行
    schedule.every(1).days.do(func)
    #添加任务按周执行
    schedule.every().weeks.do(func)
    #添加任务每周一执行,执行时间为下周一这一时刻时间
    schedule.every().monday.do(func)
     
     
    schedule.every().monday.at("12:00").do(job)
    # 每隔10分钟执行一次任务
    schedule.every(10).minutes.do(job)
    # 每隔一小时执行一次任务
    schedule.every().hour.do(job)
    # 每天10:30执行一次任务
    schedule.every().day.at("10:30").do(job)
    # 每隔5到10分钟运行一次任务?
    schedule.every(5).to(10).minutes.do(job)
    # 每周一的这个时候执行一次任务
    schedule.every().monday.do(job)
    # 每周三 13:15执行一次任务
    schedule.every().wednesday.at("13:15").do(job)
    # # 每分钟的第17秒执行任务  
    schedule.every().minute.at(":17").do(job)
     
    while True:
        schedule.run_pending()   # 检测是否有到期任务
        time.sleep(1)  # 避免一直查询cpu过高。sleep时间不应该小于执行时间,否则会线程堆积。

    BlockingScheduler: 调度器在当前进程的主线程中运行,会阻塞当前线程。

    BackgroundScheduler: 调度器在后台线程中运行,不会阻塞当前线程。

    AsyncIOScheduler: 结合asyncio模块一起使用。

    GeventScheduler: 程序中使用gevent作为IO模型和GeventExecutor配合使用。

    TornadoScheduler: 程序中使用Tornado的IO模型,用 ioloop.add_timeout 完成定时唤醒。

    TwistedScheduler: 配合TwistedExecutor,用reactor.callLater完成定时唤醒。

    QtScheduler: 应用是一个Qt应用,需使用QTimer完成定时唤醒。

    date是最基本的一种调度,作业任务只会执行一次。

    interval触发器,固定时间间隔触发。

    cron 触发器,在特定时间周期性地触发,和Linux crontab格式兼容。它是功能最强大的触发器。

    添加任务,有两种添加方法,一种add_job(), 另一种是scheduled_job()修饰器来修饰函数。

    from datetime import datetime
    from apscheduler.schedulers.blocking import BlockingScheduler 
    scheduler = BlockingScheduler() 
     
    # 第一种 
    @scheduler.scheduled_job(job_func, 'interval', seconds=10) 
    def timed_task():     
        print(datetime.now().strftime("%Y-%m-%d %H:%M:%S")) 
        
    # 第二种 
    scheduler.add_job(timed_task, 'interval', seconds=5) 
    scheduler.start() 

    删除任务,两种方法:remove_job() 和 job.remove()。remove_job()是根据任务的id来移除,所以要在任务创建的时候指定一个 id。job.remove()则是对任务执行remove方法。

    scheduler.add_job(job_func, 'interval', seconds=20, id='one') 
    scheduler.remove_job(one) 
     
    task = add_job(task_func, 'interval', seconds=2, id='job_one') 
    task.remvoe() 

    获取任务列表,通过scheduler.get_jobs()方法能够获取当前调度器中的所有任务的列表

    tasks = scheduler.get_jobs() 

    关闭任务,使用scheduler.shutdown()默认情况下调度器会等待所有正在运行的作业完成后,关闭所有的调度器和作业存储。

    scheduler.shutdown() 
    scheduler.shutdown(wait=false) 

    时区问题

    报错:ValueError: Timezone offset does not match system offset: 0 != 28800. Please, check your config files.

    分析:通过分析异常日志,发现APScheduler的默认timezone,而“0”是获取的系统环境变量的TZ时间28800对应timezone为“Asia/Shanghai”, 而0对应timezone为“UTC”,所以需将系统环境变量的时区与APScheduler的时区设置为一致。

    解决:

    #!/usr/bin/python
    # -*- coding: utf-8 -*-
     
    from apscheduler.schedulers.background import BackgroundScheduler    
    import os
     
    os.environ['TZ']= "Asia/Shanghai"
    scheduler = BackgroundScheduler(timezone="Asia/Shanghai")

    其他解决方案:

    如果部署应用dockerfile配置,也可以在dockerfile中设定系统时区。

    到此这篇关于Python快速实现定时器的五种常见方法详解的文章就介绍到这了,更多相关Python定时器内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客!

    您可能感兴趣的文章:

    • 使用Python实现一个优雅的异步定时器
    • python中定时器的高级使用方式详解
    • 详解Python定时器Timer的使用及示例
    • python实现定时器的5种方法
    • 基于Python实现简单的定时器详解
    • Python如何实现定时器功能
    • Python timer定时器两种常用方法解析

    站内搜索