2

我有一个奇怪的问题,因为我使用 Python3.9 的异步 mysql 驱动程序。我经常收到这个错误:

Task was destroyed but it is pending!
task: <ClientEventTask state=pending event=on_message coro=<bound method OnMessage.on_message of <cogs.on_message.OnMessage object at 0x7fcb96f50700>>>

而且我在谷歌中找不到很多关于这个错误的信息,我发现的唯一东西对我没有帮助。所以这个错误发生在绝对随机的时间,所以我不能完全重现它。但是如果它发生了,我可以直接看到它,因为他没有回答/计算/他应该对“消息”进行任何回复。

因此,对“on_message”任务所做的一个小解释是,如果用户发布了一条消息,则“做某事”。因此,根据用户数量和活动,它可以非常频繁或非常少地触发。我看到这个错误大多数时候出现在机器人停止/启动时,但在我的情况下,它有时会在机器人正常运行时发生。而且我不知道这个错误究竟意味着什么,我该如何解决它或它为什么会发生。我想也许是我的代码太长了,但这对我来说没有任何意义。这就是我的这个“任务”的代码:

    @commands.Cog.listener("on_message")
    async def on_message(self, message):


        if message.author.bot:
            return

        if message.type != discord.MessageType.default:
            return

        if isinstance(message.channel, discord.channel.DMChannel):
            return

        prefixes = ["!", "?", "=", ".", ",", "@", "#", "/", '§', "$", "%", "&", ":?", "*", "<", ">", "-", "bday"]
        for prefix in prefixes:
            if message.content.startswith(str(prefix)):
                return

        global member_cooldown_list
        member_cooldown_list = [i for i in member_cooldown_list if i[1] + cooldown_val > int(time.time())]
        member_index = next((i for i, v in enumerate(member_cooldown_list) if v[0] == message.author.id), None)
        if member_index is not None:
            if member_cooldown_list[member_index][1] + cooldown_val > int(time.time()):
                return

        member_cooldown_list.append((message.author.id, int(time.time())))

        count = 1
        mydb = await getConnection()
        mycursor = await mydb.cursor()
        await mycursor.execute("SELECT ignore_role_id, bonus_role_id FROM guild_role_settings WHERE guild_id = %s", (message.author.guild.id,))
        in_database = await mycursor.fetchone()
        if in_database:
            if in_database[0] is not None:
                role_list = in_database[0].split(" ")
                for roleid in role_list:
                    role = message.author.guild.get_role(int(roleid))
                    if role in message.author.roles:
                        await mycursor.close()
                        mydb.close()
                        return

            if in_database[1] is not None:
                role_list = in_database[1].split(" ")
                for roleid in role_list:
                    role = message.author.guild.get_role(int(roleid))
                    if role in message.author.roles:
                        count += 1

        await mycursor.execute("SELECT ignore_channel_id FROM guild_channel_settings WHERE guild_id = %s", (message.author.guild.id,))
        in_database1 = await mycursor.fetchone()
        if in_database1:
            if in_database1[0] is not None:
                channel_list = in_database1[0].split(" ")
                for channelid in channel_list:
                    if int(message.channel.id) == int(channelid):
                        await mycursor.close()
                        mydb.close()
                        return

        await mycursor.execute("SELECT * FROM guild_message_count WHERE guild_id = %s AND user_id = %s", (message.author.guild.id, message.author.id))
        in_database2 = await mycursor.fetchone()
        if in_database2:
            await mycursor.execute("UPDATE guild_message_count SET user_id = %s, message_count = message_count + %s WHERE guild_id = %s AND user_id = %s", (message.author.id, count, message.author.guild.id, message.author.id))
        else:
            await mycursor.execute("INSERT INTO guild_message_count (user_id, message_count, guild_id) VALUES (%s, %s, %s)", (message.author.id, count, message.author.guild.id))

        await mydb.commit()
        await mycursor.close()
        mydb.close()

如果有人向我解释错误、修复的解决方案及其发生的原因,我将不胜感激。我真的找不到太多关于它的信息,这真的很令人沮丧。特别是因为它发生在我使用异步 MySQL 驱动程序和 python3.9 之后。

2 天后 - 仍在寻找修复:自创建以来我没有找到解决方案,这真的很令人沮丧。

4

1 回答 1

1

这是因为不和谐客户端模块和消息处理线程之间的竞争条件,如链接中所述。

这是因为不和谐客户端模块每分钟左右需要控制一次。

这意味着任何窃取控制权超过一定时间的函数都会导致 discord 的客户端进入无效状态(这将在稍后的某个时间表现为异常,可能在客户端的下一个方法调用时)。

总之,您应该将所有繁重的操作卸载到单独的进程(由于 Python GIL 而不是线程)。详细情况如下:

  1. Discord 侦听器:将消息分派到工作服务器。
  2. 工作服务器:解析消息并处理 SQL。

在您的不和谐监听器中,global应避免使用以下变量:

....
@commands.Cog.listener("on_message")
async def on_message(self, message):
    import traceback
    if message.author.bot or 
        message.type != discord.MessageType.default or 
        isinstance(message.channel, discord.channel.DMChannel):
        return

    prefixes = ["!", "?", "=", ".", ",", "@", "#", "/", '§', "$", "%", "&",
                ":?", "*", "<", ">", "-", "bday"]
    for prefix in prefixes:
        if message.content.startswith(prefix):
            return
    #global member_cooldown_list
    self.member_cooldown_list = [i for i in self.member_cooldown_list
                                 if i[1] + cooldown_val > int(time.time())]
    member_index = next((i for i, v in enumerate(self.member_cooldown_list)
                         if v[0] == message.author.id), None)
    if member_index is not None:
        if self.member_cooldown_list[member_index][1] + cooldown_val > int(time.time()):
            return

    self.member_cooldown_list.append((message.author.id, int(time.time())))
    guild_id = message.author.guild.id
    try:
        response = await call("process", guild_id)
        ...
    except Exception as e:
        print (e)
        print (traceback.format_exc())

必须将以下代码移至工作服务器脚本,如下所示:

class Handler:
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    async def process(self, guild_id):
        count = 1
        mydb = await getConnection()
        ...
        await mycursor.close()
        mydb.close()
        return 0       

笔记:

  1. 您应该采用链接中的代码。
  2. nohup python the_worker_script.py &您应该使用或类似的东西运行服务器工作者脚本。
于 2021-09-16T16:15:24.790 回答