小能豆

Asyncio - 循环运行任务并使用 ctrl+C 礼貌地停止它们

py

使用 asyncio 编写 pyModbus 服务器。
除了服务器之外,我还有一个正在与之通信的串行设备和一个服务器更新任务。
一个任务应该每 500 毫秒检查一次串行设备的状态。
服务器更新任务应该检查串行设备的状态是否有任何变化并更新服务器上的信息。此外,如果服务器上有请求等待,它应该调用另一个任务,该任务将必要的信息发送到串行设备。

我的三个问题是:

  1. 我该如何礼貌地停止服务器?目前,该应用程序仅在控制台中运行,因此可以通过 ctrl+c 停止 - 我该如何停止服务器而不导致大量错误?
  2. 我如何实现周期性执行的任务(比如说我想每 500 毫秒刷新一次服务器数据)?我找到了该aiocron模块,但据我所知,它的功能有点有限,因为它仅用于间歇性调用函数。
  3. 关闭应用程序时,如何在停止服务器之前礼貌地取消所有任务(无限、循环运行的任务)?

谢谢!

编辑:

说到运行周期性任务并取消它们 - 这是正确的做法吗?这不会引发任何错误,但它能正确清理所有内容吗?(我创建了这个草图,汇集了十几个关于 stackoverflow 的问题,我不确定这是否有意义)

import asyncio

async def periodic():
    try:
        while True:
            print('periodic')
            await asyncio.sleep(1)
    except asyncio.CancelledError as ex:
        print('task1', type(ex))
        raise

async def periodic2():
    try:
        while True:
            print('periodic2')
            await asyncio.sleep(0.5)
    except asyncio.CancelledError as ex:
        print('task2', type(ex))
        raise


async def main():
            tasks = []
            task = asyncio.create_task(periodic())
            tasks.append(task)
            task2 = asyncio.create_task(periodic2())
            tasks.append(task2)
            for task in tasks:
                await task


if __name__ == "__main__":
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        pass

阅读 22

收藏
2024-12-25

共1个答案

小能豆

仅供参考:我正在逐一写下你的问题的答案,以便任何遇到这个问题的人都能理解你是如何最终得到一些编辑结果的,同时也能添加如何在不破坏任何东西的情况下取消任务的答案。

  1. 正常停止服务器:

您可以通过捕获信号(由 Ctrl+C 触发)来处理 asyncio 应用程序的正常关闭KeyboardInterrupt,然后取消正在运行的任务并关闭资源。以下是如何执行此操作的示例:

import asyncio

async def main():
    # Your other tasks and setup here

try:
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())
except KeyboardInterrupt:
    print("Received Ctrl+C. Stopping gracefully...")
    # Cancel all running tasks
    for task in asyncio.Task.all_tasks():
        task.cancel()
    # Optionally: Close any open resources (sockets, files, etc.)
    # Cleanup code here
finally:
    loop.close()
  1. 实现任务循环运行:

要使用 asyncio 循环运行任务,您可以在任务中使用 asyncio.sleep 函数。这是一个基本示例:

import asyncio

async def task_to_run():
    while True:
        # Your cyclic task code here
        await asyncio.sleep(0.5)  # Sleep for 500ms (0.5 seconds)

async def main():
    task = asyncio.create_task(task_to_run())
    # Your other tasks and setup here

此代码每 500ms 执行一次task_to_run。您可以根据需要调整睡眠时间。

  1. 礼貌地取消所有正在运行的任务:

如正常停止服务器的代码示例所示(在 Ctrl+C 处理部分),您可以使用task.cancel()它在停止服务器之前礼貌地取消所有正在运行的任务。这可确保任何正在进行的任务都有机会在退出之前进行清理

2024-12-25