如何安排任务在asyncio中使其在特定日期运行?

Lai*_*kar 5 python scheduled-tasks python-asyncio

我的程序应该运行24/7,我希望能够在某个小时/日期运行某些任务。

我已经尝试使用aiocron,但它仅支持调度功能(不支持协程),并且我读到它并不是一个很好的库。我的程序是构建的,因此我要调度的大多数(如果不是全部)任务都是在协程中构建的。

还有其他库可以进行此类任务调度吗?

否则,是否有任何使协程变形的方法,以使它们运行正常?

use*_*342 15

我已经尝试过使用 aiocron 但它只支持调度函数(不支持协程)

根据您提供的链接中的示例,情况似乎并非如此。修饰的函数@asyncio.coroutine相当于用 定义的协程async def,可以互换使用。

但是,如果您想避免 aiocron,可以直接使用asyncio.sleep将运行协程推迟到任意时间点。例如:

import asyncio, datetime

async def wait_until(dt):
    # sleep until the specified datetime
    now = datetime.datetime.now()
    await asyncio.sleep((dt - now).total_seconds())

async def run_at(dt, coro):
    await wait_until(dt)
    return await coro
Run Code Online (Sandbox Code Playgroud)

用法示例:

async def hello():
    print('hello')

loop = asyncio.get_event_loop()
# print hello ten years after this answer was written
loop.create_task(run_at(datetime.datetime(2028, 7, 11, 23, 36),
                        hello()))
loop.run_forever()
Run Code Online (Sandbox Code Playgroud)

注意:3.8 之前的 Python 版本不支持超过 24 天的睡眠间隔,因此wait_until必须解决这个限制。这个答案的原始版本是这样定义的:

async def wait_until(dt):
    # sleep until the specified datetime
    while True:
        now = datetime.datetime.now()
        remaining = (dt - now).total_seconds()
        if remaining < 86400:
            break
        # pre-3.7.1 asyncio doesn't like long sleeps, so don't sleep
        # for more than one day at a time
        await asyncio.sleep(86400)
    await asyncio.sleep(remaining)
Run Code Online (Sandbox Code Playgroud)

该限制在 Python 3.8 中被移除,并且修复被反向移植到 3.6.7 和 3.7.1。

  • 为什么不使用 [loop.call_at](https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.AbstractEventLoop.call_at)? (3认同)
  • @Vincent `loop.call_at` 接受相对于 `loop.time()` 返回的时间的时间参考,因此您仍然需要计算并且不能只给它一个例如 Unix 时间戳。第二个问题是 `call_at` 接受一个函数,而不是一个协程,所以你仍然需要一个调用 `create_task` 的蹦床。使用 `call_at`(以及 `call_later` 和 `call_soon`),您无法像编辑后的答案中那样轻松获得协程的返回值。 (2认同)
  • @Vincent 我在测试代码时发现了超时错误,方法是从现在起一个月后安排一个活动。查看[源](https://github.com/python/cpython/blob/9e9b2c32a34594e901b5b9a03c561a2a2bf63ece/Modules/selectmodule.c#L1544),限制似乎是最近的超时(以毫秒为单位)不得超过2**31 -1,这意味着如果没有像答案中的循环这样的解决方法,您的睡眠时间不能超过 24.8 天。对于 asyncio 的大多数实际用途来说,这不是问题,但对于在特定_日期_运行任务的调度程序来说,必须解决这个问题。 (2认同)
  • 有趣的是,我在 python bug 跟踪器上发现了这个[相关问题 (bpo-20423)](https://bugs.python.org/issue20493)。解决此问题的另一种方法是[安排每天唤醒的后台任务](https://gist.github.com/vxgmichel/46e7aa46d0da32ce0c647e27df39f7b9)。 (2认同)
  • 因此,睡眠不足从来都不是问题,这些超时根本不会触发,而是留待下一次等待。换句话说,循环就在那里,在**事件循环**中。*(戴上霍雷肖眼镜)* (2认同)