Discord.py 机器人,我可以做一个繁重的任务 "off to the side" 这样我就不会延迟输入吗?

Discord.py bot, can I do a heavy task "off to the side" so I don't lag inputs?

我在 Python / Discord.py 中有一个 Discord 机器人,人们可以在其中输入命令,而且机器人通常会非常快速地响应。

然而,机器人在主循环的每次迭代中也是 gathering/scraping 网络数据。通常情况下,抓取非常简短,所以没有人真正注意到,但有时代码会被设置为进行更彻底的抓取,这会花费更多时间。但是在这些繁重的抓取过程中,机器人对用户命令有点反应迟钝。

@bot.command()
async def sample_command(ctx):
    # may actually take a while for this command to respond if we happen to be
    # in the middle of a heavier site scrape
    await ctx.channel.send("Random message, something indicating bot has responded")  

async def main_loop():
    sem = asyncio.Semaphore(60)
    connector = aiohttp.TCPConnector(limit=60)

    async with aiohttp.ClientSession(connector=connector, headers=headers) as session:
        while True:
            # main guts of loop here ... 

            scrapers = [scraper_1(session, sem), scraper_2(session, sem), ...]
            data = list(chain(*await asyncio.gather(*scrapers)))  # this may take a while

            # do stuff with data

有没有办法让它继续“嘿,你想做一个沉重的刮擦,好的去别处处理它 - 同时让我们继续主循环,稍后我会与你联系大功告成,然后我们将处理数据”,如果这有意义的话?

我主要想分开这个抓取步骤,这样它就不会阻碍人们与机器人的其余部分进行实际交互的能力。

你可以尝试使用python线程。

了解更多here

它基本上允许您 运行 它在不同的线程上

示例:

import threading

def 1():
  print("Helo! This is the first thread")

def 2():
  print("Bonjour! This is the second thread")

thread1 = threading.Thread(target=1)
thread2 = Threading.Thread(target=2)

thread1.start()
thread2.start()

您可以使用 discord.py 任务扩展 docs

例如:

from discord.ext import tasks

@bot.event()
async def on_ready():
    main_loop.start()

@bot.command()
async def sample_command(ctx):
    await ctx.channel.send("Random message, something indicating bot has responded")  

@tasks.loop(seconds=60)
async def main_loop():
    do_something()

注意:不建议在on_ready开始任务,因为bot会重新连接到discord,任务会开始好几次,把它放在别的地方或者on_ready检查这是不是第一个连接。

另一个简单提示:您可以使用 await ctx.send() 而不是 await ctx.channel.send()

您可以使用 asyncio.create_task() 在“后台”生成抓取:

async def scrape_and_process(...):
    scrapers = [scraper_1(session, sem), scraper_2(session, sem), ...]
    data = list(chain(*await asyncio.gather(*scrapers)))  # this may take a while
    # do stuff with data

async def main_loop():
    sem = asyncio.Semaphore(60)
    connector = aiohttp.TCPConnector(limit=60)

    async with aiohttp.ClientSession(connector=connector, headers=headers) as session:
        while True:
            # main guts of loop here ... 

            # initiate scraping and processing in the background and
            # proceed with the loop
            asyncio.create_task(scrape_and_process(...))