Python关于定时任务

Python实现定点与定时任务方式比较多,找到下面四中实现方式,每个方式都有自己应用场景;下面来快速介绍Python中常用的定时任务实现方式:

1.循环+sleep;

2.线程模块中Timer类;

3.schedule模块;

4.定时框架:APScheduler

一、循环 + sleep

优点

适合简单的测试

缺点

只能处理单个定时任务,不容易控制,而且是个阻塞函数,time.sleep()是睡眠线程。
当前线程调用 sleep() 函数进入阻塞状态后,在其睡眠时间段内,该线程不会获得执行的机会,即使系统中没有其他可执行的线程,处于 sleep() 中的线程也不会执行,因此 sleep() 函数常用来暂停程序的运行。

代码

测试一:只能处理单个定时任务。

import time

def fun1():
    print('fun1:',time.ctime())

def run():
    while True:
        fun1()
        time.sleep(3)   # 每隔3S执行一次
run()

# 结果:
fun1: Sun Apr 26 17:57:02 2020
fun1: Sun Apr 26 17:57:05 2020
fun1: Sun Apr 26 17:57:08 2020

测试二:假如我又加进来一个函数,我想每秒都执行

import time

def fun1():
    print('fun1:',time.ctime())

def fun2():
    print('fun2:',time.ctime())

def run():
    while True:
        fun1()
        time.sleep(3)   # 每3S执行一次
        fun2()          # 每1s执行一次

run()

# 结果
fun1: Sun Apr 26 18:04:03 2020
fun2: Sun Apr 26 18:04:06 2020	# 等fun1() 执行完以后才可以执行
fun1: Sun Apr 26 18:04:06 2020
fun2: Sun Apr 26 18:04:09 2020
fun1: Sun Apr 26 18:04:09 2020
fun2: Sun Apr 26 18:04:12 2020
fun1: Sun Apr 26 18:04:12 2020
fun2: Sun Apr 26 18:04:15 2020
fun1: Sun Apr 26 18:04:15 2020

二、线程模块中Timer类

timer最基本理解就是定时器,我们可以启动多个定时任务,这些定时器任务是异步执行,所以不存在等待顺序执行问题。
定时器只能执行一次,如果需要重复执行,需要重新添加任务;

Time 模块理解

优点

非阻塞,Timer的实质是使用线程方式去执行任务,每次执行完后会销毁,所以不必担心资源问题。

缺点

不易管理多个任务

代码

测试一: Timer只能执行一次

import datetime
import time
from threading import Timer

def fun1():
    print('fun1:',time.ctime())

def fun2():
    print('fun2:',time.ctime())

#记录当前时间
print(datetime.datetime.now())
#3S执行一次
sTimer = Timer(3, fun1)
#1S执行一次
nTimer = Timer(1, fun2)
#使用线程方式执行
sTimer.start()
nTimer.start()
#等待结束
sTimer.join()
nTimer.join()
#记录结束时间
print(datetime.datetime.now())

# 结果
2020-04-26 18:42:12.933858
fun2: Sun Apr 26 18:42:13 2020
fun1: Sun Apr 26 18:42:15 2020
2020-04-26 18:42:15.934419

测试二: Timer只能执行一次,所以执行完成之后需要再次添加任务,我们对代码进行修改:

import datetime
import time
from threading import Timer

def fun1():
    print('fun1:',time.ctime())
    # 启动定时器任务,每3秒执行一次
    Timer(3, fun1).start()

def fun2():
    print('fun2:',time.ctime())
    # 启动定时器任务,每1秒执行一次
    Timer(1, fun2).start()

fun1()
fun2()

# 结果
fun1: Sun Apr 26 18:54:28 2020
fun2: Sun Apr 26 18:54:28 2020
fun2: Sun Apr 26 18:54:29 2020
fun2: Sun Apr 26 18:54:30 2020
fun1: Sun Apr 26 18:54:31 2020
fun2: Sun Apr 26 18:54:31 2020
fun2: Sun Apr 26 18:54:32 2020
fun2: Sun Apr 26 18:54:33 2020
......
无限循环下去

从时间中可以看到,这两个任务可以同时进行不存在等待问题

三、schedule模块

schedule是一个第三方轻量级的任务调度模块,可以按照秒,分,小时,日期或者自定义事件执行时间;

安装方式:
pip install schedule

优点:

可以管理和调度多个任务,可以进行控制

缺点:

阻塞式函数

代码:

普通版本,简单介绍:

import schedule
import time

def job():
    print("I'm working...")

schedule.every(10).seconds.do(job)	    # 每隔十秒钟执行一次任务
schedule.every(10).minutes.do(job)	    # 每隔十分钟执行一次任务
schedule.every(1).hour.do(job)	        # 每隔一小时执行一次任务
schedule.every().day.at("10:30").do(job)# 每天的10:30执行一次任务
schedule.every(5).to(10).days.do(job)	# 每隔5到10天执行一次任务
schedule.every().monday.do(job)         # 每周一的这个时候执行一次任务
schedule.every().wednesday.at("13:15").do(job)  # 每周三13:15执行一次任务

while True:
    schedule.run_pending()  # 运行所有可以运行的任务
    time.sleep(1)

普通版本调用,阻塞:

import schedule
import datetime
import time
import threading

def job():
    print("I'm working... in job1  start  {}".format(datetime.datetime.now()))
    time.sleep(10)
    print("I'm working... in job1  end  {}".format(datetime.datetime.now()))

def job2():
    print("I'm working... in job2        {}".format(datetime.datetime.now()))

schedule.every(3).seconds.do(job)
schedule.every(3).seconds.do(job2)

while True:
    schedule.run_pending()
    time.sleep(1)
 
# 结果 注意看 时间
I'm working... in job1  start  2020-04-29 17:26:20.229107
I'm working... in job1  end  2020-04-29 17:26:30.229383
I'm working... in job2        2020-04-29 17:26:30.229383
I'm working... in job1  start  2020-04-29 17:26:33.230687
I'm working... in job1  end  2020-04-29 17:26:43.231527
I'm working... in job2        2020-04-29 17:26:43.231527
I'm working... in job1  start  2020-04-29 17:26:46.232750
I'm working... in job1  end  2020-04-29 17:26:56.233455
I'm working... in job2        2020-04-29 17:26:56.233455
I'm working... in job1  start  2020-04-29 17:26:59.234296
I'm working... in job1  end  2020-04-29 17:27:09.235169
I'm working... in job2        2020-04-29 17:27:09.235169

改为线程版本,非阻塞:

import schedule
import datetime
import time
import threading

def job():
    print("I'm working... in job1  start  {}".format(datetime.datetime.now()))
    time.sleep(10)
    print("I'm working... in job1  end  {}".format(datetime.datetime.now()))

def job2():
    print("I'm working... in job2        {}".format(datetime.datetime.now()))


def run_threaded(job_func):
    job_thread = threading.Thread(target=job_func)
    job_thread.start()

schedule.every(3).seconds.do(run_threaded,job)
schedule.every(3).seconds.do(run_threaded,job2)

while True:
    schedule.run_pending()
    time.sleep(1)
 
# 结果 注意看  时间 和 statr 和 end
I'm working... in job1  start  2020-04-29 17:28:24.361645
I'm working... in job2        2020-04-29 17:28:24.361645
I'm working... in job1  start  2020-04-29 17:28:27.363522
I'm working... in job2        2020-04-29 17:28:27.363522
I'm working... in job1  start  2020-04-29 17:28:30.364084
I'm working... in job2        2020-04-29 17:28:30.364974
I'm working... in job1  start  2020-04-29 17:28:33.365655
I'm working... in job2        2020-04-29 17:28:33.366683
I'm working... in job1  end  2020-04-29 17:28:34.362784
I'm working... in job1  start  2020-04-29 17:28:36.367951
I'm working... in job2        2020-04-29 17:28:36.368827
I'm working... in job1  end  2020-04-29 17:28:37.365054
I'm working... in job1  start  2020-04-29 17:28:39.370776
I'm working... in job2        2020-04-29 17:28:39.371645
I'm working... in job1  end  2020-04-29 17:28:40.365915

最近有人问我while True之类的是什么意思,很简单,如果你去掉while True这个死循环的话,schedule.run_pending()是没有办法持续运行的,那么schedule.run_pending()是个什么东西呢——

schedule其实就只是个定时器。在while True死循环中,schedule.run_pending()是保持schedule一直运行,去查询上面那一堆的任务,在任务中,就可以设置不同的时间去运行。跟linux中设置crontab定时任务是类似的。

所以,schedule有一定的局限性,所以只能用来执行一些小型的定时任务,它的局限性在哪呢——

1.需要定时运行的函数job不应当是死循环类型的,也就是说,这个线程应该有一个执行完毕的出口。一是因为线程万一僵死,会是非常棘手的问题;二是下一次定时任务还会开启一个新的线程,执行次数多了就会演变成灾难。

2.如果schedule的时间间隔设置得比job执行的时间短,一样会线程堆积形成灾难,也就是说,我job的执行时间是1个小时,但是我定时任务设置的是5分钟一次,那就会一直堆积线程。

四、定时框架:APScheduler

APScheduler是Python的一个定时任务框架,用于执行周期或者定时任务,
可以基于日期、时间间隔,及类似于Linux上的定时任务crontab类型的定时任务;
该该框架不仅可以添加、删除定时任务,还可以将任务存储到数据库中,实现任务的持久化,使用起来非常方便。

安装方式:pip install apscheduler

apscheduler组件及简单说明:

1>triggers(触发器):触发器包含调度逻辑,每一个作业有它自己的触发器
2>job stores(作业存储):用来存储被调度的作业,默认的作业存储器是简单地把作业任务保存在内存中,支持存储到MongoDB,Redis数据库中
3> executors(执行器):执行器用来执行定时任务,只是将需要执行的任务放在新的线程或者线程池中运行
4>schedulers(调度器):调度器是将其它部分联系在一起,对使用者提供接口,进行任务添加,设置,删除。

interval 触发器
参数 说明
weeks (int) 间隔几周
days (int) 间隔几天
hours (int) 间隔几小时
minutes (int) 间隔几分钟
seconds (int) 间隔多少秒
start_date (datetime 或 str) 开始日期
end_date (datetime 或 str) 结束日期
timezone (datetime.tzinfo 或str) 时区

cron 触发器:
参数 说明
year (int 或 str) 年,4位数字
month (int 或 str) 月 (范围1-12)
day (int 或 str) 日 (范围1-31
week (int 或 str) 周 (范围1-53)
day_of_week (int 或 str) 周内第几天或者星期几 (范围0-6 或者 mon,tue,wed,thu,fri,sat,sun)
hour (int 或 str) 时 (范围0-23)
minute (int 或 str) 分 (范围0-59)
second (int 或 str) 秒 (范围0-59)
start_date (datetime 或 str) 最早开始日期(包含)
end_date (datetime 或 str) 最晚结束时间(包含)
timezone (datetime.tzinfo 或str) 指定时区

优点:

实现简单,只需要直接创建任务,并将添加到调度器中即可。

缺点:

。。。。。

代码:

普通版本调用:

import schedule
import datetime
import time
import threading
from apscheduler.schedulers.background import BlockingScheduler

def job():
    print("I'm working... in job1  start  {}".format(datetime.datetime.now()))
    # time.sleep(5)
    print("I'm working... in job1  end  {}".format(datetime.datetime.now()))

def job2():
    print("I'm working... in job2        {}".format(datetime.datetime.now()))

if __name__ == '__main__':
    scheduler = BlockingScheduler()
    scheduler.add_job(job, 'interval', seconds=3)  # 间隔3秒钟执行一次
    scheduler.add_job(job2,'interval', seconds=3)  # 间隔3秒钟执行一次
    scheduler.start()  # 这里的调度任务是独立的一个线程

# 结果
I'm working... in job1  start  2020-04-29 17:54:12.657444
I'm working... in job1  end  2020-04-29 17:54:12.657444
I'm working... in job2        2020-04-29 17:54:12.657444
I'm working... in job1  start  2020-04-29 17:54:15.658007
I'm working... in job1  end  2020-04-29 17:54:15.658324
I'm working... in job2        2020-04-29 17:54:15.658554

运行时间大于调用时间:

import schedule
import datetime
import time
import threading
from apscheduler.schedulers.background import BlockingScheduler

def job():
    print("I'm working... in job1  start  {}".format(datetime.datetime.now()))
    time.sleep(5)
    print("I'm working... in job1  end  {}".format(datetime.datetime.now()))

def job2():
    print("I'm working... in job2        {}".format(datetime.datetime.now()))

if __name__ == '__main__':
    scheduler = BlockingScheduler()
    scheduler.add_job(job, 'interval', seconds=3)  # 间隔3秒钟执行一次
    scheduler.add_job(job2,'interval', seconds=3)  # 间隔3秒钟执行一次
    scheduler.start()  # 这里的调度任务是独立的一个线程

# 结果
I'm working... in job1  start  2020-04-29 17:50:17.710468
I'm working... in job2        2020-04-29 17:50:17.726393
Execution of job "job (trigger: interval[0:00:03], next run at: 2020-04-29 17:50:20 CST)" skipped: maximum number of running instances reached (1)
I'm working... in job2        2020-04-29 17:50:20.724602
I'm working... in job1  end  2020-04-29 17:50:22.711978
I'm working... in job1  start  2020-04-29 17:50:23.710288
I'm working... in job2        2020-04-29 17:50:23.727116

假如:我们在执行任务的时候,这个任务的执行时间为5秒,但是调用它是没3秒调用一次,然后会报错。
这是因为任务重复了,看这个解释:

https://pdf-lib.org/Home/Details/10550

我们将这个改了,就好了 ;但是假如执行时间还是很长,那还是把调用时间延长吧。

scheduler.add_job(func2, ‘interval’,max_instances=5, seconds=3)

解决运行时间大于调用时间:

import schedule
import datetime
import time
import threading
from apscheduler.schedulers.background import BlockingScheduler

def job():
    print("I'm working... in job1  start  {}".format(datetime.datetime.now()))
    time.sleep(5)
    print("I'm working... in job1  end  {}".format(datetime.datetime.now()))

def job2():
    print("I'm working... in job2        {}".format(datetime.datetime.now()))

if __name__ == '__main__':
    scheduler = BlockingScheduler()
    scheduler.add_job(job, 'interval', max_instances=3, seconds=3)  # 间隔3秒钟执行一次,允许重复3个相同任务
    scheduler.add_job(job2, 'interval',max_instances=3, seconds=3)  # 间隔3秒钟执行一次,允许重复3个相同任务
    scheduler.start()  # 这里的调度任务是独立的一个线程

# 结果 注意 satrt end 时间
I'm working... in job1  start  2020-04-29 17:48:29.388417
I'm working... in job2        2020-04-29 17:48:29.388417
I'm working... in job1  start  2020-04-29 17:48:32.389530
I'm working... in job2        2020-04-29 17:48:32.390525
I'm working... in job1  end  2020-04-29 17:48:34.389543
I'm working... in job1  start  2020-04-29 17:48:35.390585
I'm working... in job2        2020-04-29 17:48:35.391533
I'm working... in job1  end  2020-04-29 17:48:37.390578
I'm working... in job1  start  2020-04-29 17:48:38.389937
I'm working... in job2        2020-04-29 17:48:38.390813
I'm working... in job1  end  2020-04-29 17:48:40.390838

假如:你的运行时间很大。。。那直接将调用时间延长吧

启动APScheduler进行定时任务
一、运行计划任务的python脚本
如果我们在终端中直接执行的话,关闭终端窗口,Python任务就会中断,Python进程会被杀死,程序将停止运行。可以使用如下命令运行python脚本,

python apschedulerscript.py &

这样执行后及时关闭终端窗口,程序依旧运行。
二、停止计划任务的Python脚本
如何停止呢,可使用如下方法:
其实在执行命令:python apschedulerscript.py &之后会在控制台输出改进程id
例如:

[1] 3057

直接只用kill命令结束即可

kill 3057

如果运行后忘记进程ID,则可遵循下面的方法进行停止

ps -e | grep python

这样将会在终端列出python相关的进程。

916 ? 00:04:05 python
3057 pts/0 00:00:00 python

在这里插入图片描述
使用文件名查找
tasklist | findstr python


可以参考这个文章:

1、https://mp.weixin.qq.com/s/TTUFQRQ_DiKktJ5-J8O8Pg
2、https://blog.csdn.net/wuyongpeng0912/article/details/88895659


关于APScheduler中两种调度器的区别及使用过程中要注意的问题

https://blog.csdn.net/ybdesire/article/details/82228840


关于启动任务

https://www.cnblogs.com/jinxiao-pu/p/9131057.html


apscheduler官网

https://apscheduler.readthedocs.io/en/stable/


备注:
1、当我们使用BcakgroundScheduler()的时候,线程模式,注意while(true)

使用while(true)的作用:
run方法中的代码就是线程要运行的代码,运行完毕以后,就不会再次运行,其方法本身并不是无限循环的。而while(true)是为了让run方法中的代码不断重复的运行,也就是让线程不停的运行,便于查看效果。如果去掉,run运行结束,线程也就结束了。
当然,while(true)并不是run()方法必须的,如果线程的run()部分只需要执行一次,则不需要嵌套while(true)循环。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值