【问题标题】:Why does discord.py block/buffer when awaiting an asyncio Queue?为什么在等待异步队列时 discord.py 会阻塞/缓冲?
【发布时间】:2020-02-07 18:46:01
【问题描述】:

消息以“波”的形式发送,即几秒钟内什么都没有,然后几乎同时有大约 5 秒钟。我漏掉了tokenchannel

import discord, asyncio

class Bot(discord.Client):
    def __init__(self, q, channel):
        super().__init__()
        self.q = q
        self.channel_id = channel
        self.bg_task = self.loop.create_task(self.send_messages())

    async def on_message(self, message):
        if message.author == self.user or message.channel.id != self.channel_id:
            return
        print(message.content)

    async def send_messages(self):
        await self.wait_until_ready()
        channel = self.get_channel(self.channel_id)
        while not self.is_closed():
            msg = await self.q.get()
            await channel.send(msg)

from threading import Thread
from time import sleep

q = asyncio.Queue()
def f():
    while True:
        q.put_nowait("hi")
        sleep(2)
Thread(target=f).start()

bot = Bot(q, channel)
bot.run(token)

奇怪的是,on_message 事件似乎不受影响,并且将msg = await self.q.get() 替换为

msg = "hi"
await asyncio.sleep(2)

似乎会导致预期的行为。

我不确定哪里出了问题,所以我将示例更具体地用于 Discord。

编辑

扩展asyncio.sleep 行为,我已将send_messages 中的循环替换为

if 0:
    msg = await self.q.get()
else:
    await asyncio.sleep(0.1)
    if self.q.empty():
        continue
    msg = await self.q.get()
await channel.send(msg)

if 只是在原始和实验之间切换。

显然,人们预计else 部分最多与if 部分一样快,但是等待队列非空似乎可以完全解决问题。

我开始认为不和谐和阻塞异步队列以不可预见的方式交互

另一方面,channel.send 似乎是阻塞线,所以也许它也与速率有关。

【问题讨论】:

  • 这只是 discord api 的内部工作原理,asyncio 本身不是问题
  • @LuM 我怀疑是这样的,但为什么不同样适用于 asyncio.sleep?

标签: python python-3.x python-asyncio discord.py


【解决方案1】:

这是因为通过 Discord 的 API 发送消息的速率限制为 5 / 5 秒。
这就是为什么您会看到一次发送 5 条消息,然后由于下一条消息的速率受到限制而延迟。

【讨论】:

  • 感谢您的回答,但我认为这不是唯一的问题。请看我的编辑。当发送消息的频率较低(例如每 10 秒一次)或不规则(这是实际应用)发送消息时也会出现此效果
  • 啊,这可能是因为asyncio.Queue 不是线程安全的。您应该尝试使用loop.call_soon_threadsafeasyncio.run_coroutine_threadsafe
  • 是的,这是有道理的。只是一个小问题,我已经用msg = asyncio.run_coroutine_threadsafe(self.q.get(), self.loop).result() 替换了msg = await self.q.get(),但是如果我在此期间不等待某些东西,结果部分似乎会永远阻塞(即使队列非空)。我错过了什么?
  • 好吧,很明显.result() 阻塞了事件循环,因此self.q.get() 无法运行。在我看来asyncio.run_coroutine_threadsafe 只对另一个线程有用?如果我将排队部分放在线程安全函数中可能会有所帮助,但是这是不可行的,因为它发生在程序的完全不同的部分。还有其他不涉及额外线程的想法吗?
猜你喜欢
  • 2019-10-16
  • 1970-01-01
  • 2022-01-04
  • 2017-11-11
  • 2016-03-13
  • 1970-01-01
  • 2011-04-03
  • 1970-01-01
  • 2021-10-06
相关资源
最近更新 更多