【问题标题】:python3.9 and mysql async - Why i'm getting "Task was destroyed but it is pending!"?python3.9 和 mysql 异步 - 为什么我得到“任务被破坏但它正在等待处理!”?
【发布时间】:2021-11-08 07:18:53
【问题描述】:

我遇到了一个奇怪的问题,因为我在 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 天后 - 仍在寻找解决方案: 自创建以来我没有找到解决方案,这真的很令人沮丧。

【问题讨论】:

    标签: python python-3.x async-await aio-mysql


    【解决方案1】:

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

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

    这意味着任何窃取控制权超过 特定时间会导致 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. 您应该采用link中的代码。
    2. 您应该使用nohup python the_worker_script.py &amp; 或类似的东西来运行服务器工作者脚本。

    【讨论】:

    • 修复看起来如何?我真的不明白,这让我很困惑。我从来没有处理过这样的错误。
    • @Razzer 由于我无法访问您的完整代码,因此我无法自己测试代码,实际上这是您的工作。我已经按照你原来的代码写了一个骨架代码。
    猜你喜欢
    • 1970-01-01
    • 2016-02-29
    • 2017-01-08
    • 2016-06-25
    • 2019-06-15
    • 1970-01-01
    • 2019-08-09
    • 2018-05-30
    • 1970-01-01
    相关资源
    最近更新 更多