python实现定时任务:schedule库、APScheduler库

文章目录

一、简单实现

1、sleep

精度低(受系统调度影响)、阻塞主线程、程序退出后任务停止。

py 复制代码
import time

# 定义要定时执行的任务
def task():
    print(f"任务执行时间:{time.strftime('%Y-%m-%d %H:%M:%S')}")

# 定时任务主逻辑(每隔5秒执行一次)
if __name__ == "__main__":
    interval = 5  # 执行间隔(秒)
    while True:
        task()
        # 等待指定时间后再执行下一次
        time.sleep(interval)

2、crontab系统级

bash 复制代码
# 编辑crontab规则
crontab -e
# 添加规则(每分钟执行test.py)
* * * * * /usr/bin/python3 /path/to/test.py

二、schedule库

官方文档:https://schedule.readthedocs.io/en/stable/index.html

bash 复制代码
# 安装
pip install schedule

1、使用

schedule.every(时间).单位.do(任务函数):核心语法,定义定时规则;
schedule.run_pending():检查并执行到期的任务;

优点:语法直观、支持丰富的定时规则(按秒 / 分 / 时 / 日 / 周 / 月);

缺点:仍需主线程循环、不支持任务持久化、无法处理任务并发。

py 复制代码
import schedule
import time
import datetime

# 定义定时任务
def daily_task():
    print(f"每日任务执行:{datetime.datetime.now()}")

def every_10_seconds_task():
    print(f"每10秒任务执行:{datetime.datetime.now()}")

# 配置定时规则
if __name__ == "__main__":
    # 1. 每隔10秒执行一次
    schedule.every(10).seconds.do(every_10_seconds_task)
    # 2. 每天固定时间(比如8点30分)执行
    schedule.every().day.at("08:30").do(daily_task)
    # 3. 每周一执行
    schedule.every().monday.do(daily_task)
    # 4. 每分钟的第15秒执行
    schedule.every().minute.at(":15").do(every_10_seconds_task)

    # 持续运行调度器
    while True:
        schedule.run_pending()  # 检查是否有任务需要执行
        time.sleep(1)  # 每秒检查一次

三、APScheduler库

官方文档:https://pypi.org/project/APScheduler/

文档:https://apscheduler.readthedocs.io/en/master/?badge=3.x

bash 复制代码
# 安装
pip install apscheduler

APScheduler的核心由4部分组成,理解它们是使用的基础:
调度器(Scheduler) :整个库的核心,负责管理任务的注册、触发、执行。不同调度器适配不同场景(见下文案例)。
触发器(Trigger) :定义任务的执行时间规则,核心有3种:
date:一次性触发(指定具体时间执行1次)。
interval:固定时间间隔触发(如每5分钟执行)。
cron:类Unix cron表达式触发(支持复杂规则,如每周一8点)。
作业存储(Job Store) :保存任务的信息,默认是MemoryJobStore(内存存储,重启丢失),也支持Redis/MongoDB/SQLAlchemy(数据库)等持久化存储。
执行器(Executor) :负责执行任务,默认ThreadPoolExecutor(线程池),也可使用ProcessPoolExecutor(进程池,适合CPU密集型任务)。

场景1:一次性任务(date触发器)

适用场景:需要在某个具体时间点执行一次的任务(如2025年1月1日0点执行新年祝福发送、定时执行一次数据备份)。

python 复制代码
from apscheduler.schedulers.blocking import BlockingScheduler
from datetime import datetime, timedelta

# 定义要执行的任务
def one_time_task(name):
    print(f"一次性任务执行:{name},当前时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")

# 创建阻塞式调度器(适用于独立脚本)
scheduler = BlockingScheduler()

# 添加一次性任务:3秒后执行(也可指定具体时间,如datetime(2025, 1, 1, 0, 0, 0))
run_time = datetime.now() + timedelta(seconds=3)
scheduler.add_job(
    one_time_task,          # 要执行的函数
    'date',                 # 触发器类型
    run_date=run_time,      # 执行时间(datetime对象/字符串均可)
    args=["新年数据备份"],   # 传递给函数的参数
    id="one_time_job"       # 任务ID,用于后续管理
)

print(f"调度器已启动,任务将在 {run_time.strftime('%Y-%m-%d %H:%M:%S')} 执行")

try:
    scheduler.start()  # 启动调度器(阻塞主线程)
except KeyboardInterrupt:
    # 按Ctrl+C停止调度器
    scheduler.shutdown()
    print("调度器已停止")

BlockingScheduler是阻塞式调度器,启动后主线程被占用,适合独立的定时脚本。
run_date支持字符串格式(如"2025-01-01 00:00:00"),无需手动构建datetime对象。

场景2:固定时间间隔任务(interval触发器)

适用场景:每隔固定时间执行一次的任务(如每5分钟检查服务器状态、每1小时同步一次数据)。

python 复制代码
from apscheduler.schedulers.blocking import BlockingScheduler
from datetime import datetime, timedelta

# 定义间隔执行的任务
def interval_task():
    print(f"间隔任务执行,当前时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")

scheduler = BlockingScheduler()

# 添加间隔任务:每2秒执行一次,20秒后停止
scheduler.add_job(
    interval_task,
    'interval',
    seconds=2,               # 间隔单位(支持seconds/minutes/hours/days/weeks)
    start_date=datetime.now(),  # 开始时间(默认立即开始)
    end_date=datetime.now() + timedelta(seconds=20),  # 结束时间
    max_instances=1,         # 最多同时运行1个实例(避免任务叠加)
    id="interval_job"
)

print("间隔任务调度器已启动(每2秒执行,20秒后停止),按Ctrl+C退出")
try:
    scheduler.start()
except KeyboardInterrupt:
    scheduler.shutdown()
    print("调度器已停止")

max_instances是核心参数,避免短间隔任务(如1秒)未执行完又触发新实例,导致资源耗尽。

支持start_date/end_date精准控制任务的生命周期。

场景3:CRON表达式任务(cron触发器)

适用场景:复杂时间规则的定时任务(如每天8点打卡、每周一18点统计数据、每月1号凌晨2点备份),是最常用的触发器。

CRON表达式格式(与Linux cron一致):秒 分 时 日 月 周 年(年可选),支持通配符*(任意)、*/n(每隔n)、-(范围)、,(枚举)。

python 复制代码
from apscheduler.schedulers.blocking import BlockingScheduler
from datetime import datetime

# 定义CRON任务
def cron_task():
    print(f"CRON任务执行,当前时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")

scheduler = BlockingScheduler()

# 案例1:每分钟的第30秒执行(如 10:00:30、10:01:30...)
scheduler.add_job(cron_task, 'cron', second=30, id="cron_job1")

# 案例2:每天早上8点整执行(取消注释即可测试)
# scheduler.add_job(cron_task, 'cron', hour=8, minute=0, second=0, id="cron_job2")

# 案例3:每周一、三、五的18:30:00执行
# scheduler.add_job(cron_task, 'cron', day_of_week='1,3,5', hour=18, minute=30, second=0, id="cron_job3")

# 案例4:每月1号和15号的凌晨2点执行
# scheduler.add_job(cron_task, 'cron', day='1,15', hour=2, minute=0, second=0, id="cron_job4")

print("CRON任务调度器已启动(每分钟30秒执行),按Ctrl+C退出")
try:
    scheduler.start()
except KeyboardInterrupt:
    scheduler.shutdown()
    print("调度器已停止")

常用CRON参数速查:
second:秒(0-59)
minute:分(0-59)
hour:时(0-23)
day:日(1-31)
month:月(1-12)
day_of_week:周(0-6,0=周日,或用mon/tue/wed等)

场景4:带参数的定时任务

适用场景:任务需要动态传入参数(如根据不同用户ID发送提醒、处理不同数据源)。

python 复制代码
from apscheduler.schedulers.blocking import BlockingScheduler
from datetime import datetime

# 带参数的任务函数
def param_task(user_id, message):
    print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] 向用户 {user_id} 发送消息:{message}")

scheduler = BlockingScheduler()

# 方式1:位置参数(args)
scheduler.add_job(
    param_task,
    'interval',
    seconds=3,
    args=[1001, "您的订单已发货"],  # 列表形式传递位置参数
    id="param_job1"
)

# 方式2:关键字参数(kwargs)
scheduler.add_job(
    param_task,
    'interval',
    seconds=3,
    kwargs={"user_id": 1002, "message": "您的会员即将到期"},  # 字典形式传递关键字参数
    id="param_job2"
)

print("带参数的任务调度器已启动,按Ctrl+C退出")
try:
    scheduler.start()
except KeyboardInterrupt:
    scheduler.shutdown()
    print("调度器已停止")

argskwargs可单独/结合使用,但需保证参数与任务函数的定义匹配。

场景5:任务的暂停、恢复、移除

适用场景:动态管理任务(如临时暂停某个任务排查问题、不再需要的任务直接移除)。

python 复制代码
from apscheduler.schedulers.blocking import BlockingScheduler
from datetime import datetime
import time

# 测试任务
def dynamic_task():
    print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] 动态任务执行")

scheduler = BlockingScheduler()

# 添加任务
scheduler.add_job(dynamic_task, 'interval', seconds=2, id="dynamic_job")

# 启动调度器
scheduler.start(paused=False)
print("任务已启动,5秒后暂停...")
time.sleep(5)

# 暂停任务(通过任务ID)
scheduler.pause_job("dynamic_job")
print("任务已暂停,5秒后恢复...")
time.sleep(5)

# 恢复任务
scheduler.resume_job("dynamic_job")
print("任务已恢复,5秒后移除...")
time.sleep(5)

# 移除任务
scheduler.remove_job("dynamic_job")
print("任务已移除,5秒后停止调度器...")
time.sleep(5)

# 停止调度器
scheduler.shutdown()
print("调度器已停止")

核心方法:
pause_job(job_id):暂停指定任务
resume_job(job_id):恢复暂停的任务
remove_job(job_id):永久移除任务
get_job(job_id):获取任务详情(返回Job对象)

场景6:非阻塞调度器(BackgroundScheduler)

适用场景:Web应用(Flask/Django)、后台服务(不希望阻塞主线程)。

python 复制代码
from apscheduler.schedulers.background import BackgroundScheduler
from datetime import datetime
import time

# 后台任务
def background_task():
    print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] 后台任务执行")

# 创建后台调度器(不阻塞主线程)
scheduler = BackgroundScheduler()

# 添加间隔任务
scheduler.add_job(background_task, 'interval', seconds=2, id="background_job")

# 启动调度器(非阻塞,主线程继续执行)
scheduler.start()

print("后台调度器已启动,主线程继续执行其他逻辑...")

# 主线程模拟Web服务的核心逻辑
try:
    while True:
        time.sleep(1)
        print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] 主线程运行中...")
except KeyboardInterrupt:
    scheduler.shutdown()
    print("调度器已停止,主线程退出")

BackgroundScheduler启动后不阻塞主线程,是Web应用的首选。

主线程退出后,后台调度器也会停止,需保证主线程持续运行(如Flask的app.run())。

场景7:任务异常处理

适用场景:避免单个任务出错导致调度器崩溃,或记录任务异常日志。

python 复制代码
from apscheduler.schedulers.blocking import BlockingScheduler
from datetime import datetime
import logging

# 配置日志(记录异常)
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

# 模拟会抛出异常的任务
def error_task():
    logger.info("开始执行可能出错的任务")
    1 / 0  # 故意触发除零异常
    logger.info("任务执行完成")

# 自定义异常监听器
def job_exception_listener(event):
    if event.exception:
        logger.error(f"任务 {event.job_id} 执行失败:{event.exception}", exc_info=True)
    else:
        logger.info(f"任务 {event.job_id} 执行成功")

scheduler = BlockingScheduler()

# 添加任务
scheduler.add_job(error_task, 'interval', seconds=3, id="error_job")

# 监听任务执行事件(成功/失败)
scheduler.add_listener(job_exception_listener, eventtypes=['job_executed', 'job_error'])

print("带异常处理的调度器已启动,按Ctrl+C退出")
try:
    scheduler.start()
except KeyboardInterrupt:
    scheduler.shutdown()
    print("调度器已停止")

即使任务抛出异常,调度器仍会继续运行,不会整体崩溃。

通过eventtypes可精准监听指定类型的事件(如仅监听错误job_error)。

场景8:任务持久化(SQLAlchemyJobStore)

适用场景:调度器重启后任务不丢失(如服务重启后,定时任务自动恢复)。

python 复制代码
from apscheduler.schedulers.blocking import BlockingScheduler
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
from datetime import datetime

# 配置作业存储:使用SQLite数据库(支持MySQL/PostgreSQL)
job_stores = {
    'default': SQLAlchemyJobStore(url='sqlite:///jobs.db')  # 任务存储在jobs.db文件中
}

# 创建调度器并指定作业存储
scheduler = BlockingScheduler(jobstores=job_stores)

# 定义持久化任务
def persistent_task():
    print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] 持久化任务执行")

# 添加任务(自动保存到数据库)
scheduler.add_job(
    persistent_task,
    'interval',
    seconds=3,
    id="persistent_job",
    replace_existing=True  # 避免重复添加同名任务
)

print("持久化任务调度器已启动,任务信息保存在jobs.db中,按Ctrl+C退出")
try:
    scheduler.start()
except KeyboardInterrupt:
    scheduler.shutdown()
    print("调度器已停止,任务信息仍保存在数据库中,重启后可恢复")

支持多种数据库:

MySQL:mysql+pymysql://user:password@host:port/dbname

PostgreSQL:postgresql://user:password@host:port/dbname

重启调度器后,任务会从数据库自动加载,无需重新注册。

相关推荐
Dfreedom.2 小时前
从 model(x) 到__call__:解密深度学习框架的设计基石
人工智能·pytorch·python·深度学习·call
weixin_440730502 小时前
java数组整理笔记
java·开发语言·笔记
weixin_425023002 小时前
Spring Boot 配置文件优先级详解
spring boot·后端·python
Thera7772 小时前
状态机(State Machine)详解:原理、优缺点与 C++ 实战示例
开发语言·c++
niucloud-admin3 小时前
java服务端——controller控制器
java·开发语言
小徐Chao努力3 小时前
【Langchain4j-Java AI开发】06-工具与函数调用
java·人工智能·python
无心水3 小时前
【神经风格迁移:全链路压测】33、全链路监控与性能优化最佳实践:Java+Python+AI系统稳定性保障的终极武器
java·python·性能优化
夏幻灵4 小时前
JAVA基础:基本数据类型和引用数据类型
java·开发语言
luoluoal4 小时前
基于python的小区监控图像拼接系统(源码+文档)
python·mysql·django·毕业设计·源码