当前位置:首页 > 技术 > 正文内容

利用Signal信号控制AsyncIOScheduler定时关闭

访客 技术 2026年6月17日 2

在使用 aiohttpAPSchedulerAsyncIOScheduler 实现异步定时任务时,常会遇到程序提前退出或无法精确控制调度器生命周期的问题。尤其是在主函数中调用 scheduler.start() 后,事件循环未持续运行,导致添加的任务未能执行即退出。

典型的场景是:向调度器注册多个延迟执行的异步任务后,期望在所有任务执行完毕或经过指定时间后安全关闭调度器。但由于缺乏持续的事件循环驱动,程序立即终止,任务无法完成。

问题分析

AsyncIOScheduler 基于 asyncio 事件循环工作,调用 start() 并不会阻塞主线程。若不手动维持事件循环运行,Python 主程序将直接结束。常见的解决方式是调用:

asyncio.get_event_loop().run_forever()

但这会使程序无限挂起,除非外部强制中断(如 Ctrl+C)。为了实现"运行一段时间后自动退出",需要一种机制在特定时间触发退出动作。

使用 Signal 实现定时退出

在 Unix-like 系统中,signal 模块可用于注册信号处理器。通过 signal.alarm() 可设置一个定时器,在指定秒数后发送 SIGALRM 信号,从而触发预定义的清理逻辑。

结合此机制,可在调度器启动后设定一个超时,到达时间后由信号处理函数主动停止事件循环和调度器。

改进后的实现

import asyncio
import signal
from datetime import datetime, timedelta

import aiohttp
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore


async def fetch_get(session):
    url = 'https://httpbin.org/get?a=1'
    async with session.get(url) as response:
        print(f'GET 请求状态: {response.status}')
        return await response.text()


async def fetch_post(session):
    url = 'https://httpbin.org/post?b=2'
    async with session.post(url) as response:
        print(f'POST 请求状态: {response.status}')
        return await response.text()


async def task_runner():
    async with aiohttp.ClientSession() as client:
        await fetch_get(client)
        await fetch_post(client)


def schedule_shutdown(signum, frame):
    """信号处理函数:接收到 SIGALRM 时停止调度器并关闭事件循环"""
    print("收到终止信号,正在关闭调度器...")
    scheduler = AsyncIOScheduler.get_instance()
    if scheduler.running:
        scheduler.shutdown(wait=True)
    loop = asyncio.get_event_loop()
    loop.stop()


if __name__ == '__main__':
    # 设置信号处理器
    signal.signal(signal.SIGALRM, schedule_shutdown)
    
    # 配置持久化任务存储
    stores = {'default': SQLAlchemyJobStore(url='sqlite:///jobs.sqlite')}
    scheduler = AsyncIOScheduler(jobstores=stores)
    
    # 添加10个延时10秒执行的任务
    for _ in range(10):
        scheduler.add_job(task_runner, 'date', run_date=datetime.now() + timedelta(seconds=10))
    
    scheduler.start()
    
    # 设定20秒后发送 SIGALRM 信号
    signal.alarm(20)
    
    # 启动事件循环等待任务执行及信号触发
    print("调度器已启动,将在20秒后自动关闭...")
    asyncio.get_event_loop().run_forever()

关键点说明

  • 信号绑定:通过 signal.signal(SIGALRM, handler) 注册处理函数,确保定时器触发时能执行清理逻辑。
  • 安全关闭scheduler.shutdown(wait=True) 确保正在运行的任务有机会完成。
  • 跨平台限制signal.alarm() 仅适用于 Unix 系统(Linux/macOS),Windows 下不可用。生产环境需考虑替代方案如异步定时检查或使用 asyncio.sleep() 控制生命周期。

替代思路(纯异步方式)

为避免依赖系统信号,也可采用纯协程方式实现延时关闭:

async def auto_shutdown(delay):
    await asyncio.sleep(delay)
    print("自动关闭调度器...")
    scheduler.shutdown(wait=True)
    loop = asyncio.get_event_loop()
    loop.stop()

# 在 start 后调度该协程
loop = asyncio.get_event_loop()
loop.create_task(auto_shutdown(20))
loop.run_forever()

这种方式更具可移植性,推荐用于跨平台项目。

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。