每隔X秒调度Asyncio任务执行?

12

我正在尝试创建一个Python Discord机器人,每X秒检查活跃成员并奖励在线时间的积分。我正在使用asyncio处理聊天命令,这一切都在运作。我的问题是找到一种异步方式,以便每隔X秒计划检查活动成员。

我已阅读过asyncio文档,但这是我第一次使用它,我很难理解任务、循环、协程等方面的内容。

@client.event
async def on_message(message):

    # !gamble command
    if message.content.startswith('!gamble'):

        ...code that works....

    # !help command
    elif message.content == '!help':

         ...code that works....

    # !balance command
    elif message.content == '!balance':

      ...code that works....

@client.event
async def on_ready():
    print('Logged in as')
    print(client.user.name)
    print(client.user.id)
    print('------')

# Do this every X seconds to give online users +1 points
async def periodic_task():
      TODO

我的目标是让机器人能够通过聊天处理给定的命令,同时每隔 X 秒触发一个与 Discord 服务器中聊天命令或事件无关的函数。我知道如何使函数内的代码实现我的目标,但不知道如何触发它。

2个回答

17

如果您想确保执行时间不会导致间隔偏差,可以使用asyncio.gather。

import asyncio, time, random


start_time = time.time()


async def stuff():
    await asyncio.sleep(random.random() * 3)
    print(round(time.time() - start_time, 1), "Finished doing stuff")


async def do_stuff_periodically(interval, periodic_function):
    while True:
        print(round(time.time() - start_time, 1), "Starting periodic function")
        await asyncio.gather(
            asyncio.sleep(interval),
            periodic_function(),
        )


asyncio.run(do_stuff_periodically(5, stuff))

输出结果如下:

0.0 Starting periodic function
0.5 Finished doing stuff
5.0 Starting periodic function
7.2 Finished doing stuff
10.0 Starting periodic function
10.1 Finished doing stuff
15.0 Starting periodic function
17.9 Finished doing stuff

正如你所看到的,所调用的周期性函数的执行时间不会影响新间隔的开始时间。


1
我该如何让 do_stuff_periodically 在稍后停止? - Jack Deeth
1
有什么办法可以避免它仍然存在的微小漂移吗?它在每个周期中都会失去几毫秒。 - filippo
@filippo 你确定存在实际漂移并且测量准确吗? - marcoc88
@filippo 可能会发生漂移。Asyncio 不是确定性的,它取决于您的代码和运行时发生的事情。Python 尽最大努力尽可能好地安排所有异步任务的调度。 - zonk

11
async def do_stuff_every_x_seconds(timeout, stuff):
    while True:
        await asyncio.sleep(timeout)
        await stuff()

并将此添加到循环中。

task = asyncio.create_task(do_stuff_every_x_seconds(10, stuff))

当你不再想做那件事时,

task.cancel()

大家注意,这个解决方案只适用于Python >3.7。 - v1vendi
1
这个程序在依次运行 stuffsleep 时出现了偏移,@marcoc88 提供的使用 asyncio.gather 的解决方案似乎能解决问题。 - filippo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接