如何避免Discord Python Bot因长耗时await函数触发心跳阻塞警告?
解决Discord Python Bot心跳阻塞警告的方案
你的问题根源是check_all函数执行耗时过长,阻塞了Discord.py的事件循环。Discord Bot需要定期发送心跳包维持连接,一旦事件循环被卡住超过10秒,就会触发heartbeat blocked警告。
下面是具体的解决方案:
把耗时的check_all函数放到独立线程中执行,让事件循环能继续处理心跳和其他任务,同时在函数执行完成后自动获取结果并发送消息。
代码修改示例(Python 3.9+)
使用asyncio.to_thread直接将同步阻塞函数放到线程池执行:
import asyncio from datetime import datetime import discord from discord.ext import tasks @tasks.loop(minutes=5) async def send_message(): channel = discord.utils.get(client.get_all_channels(), name="bremen") current_time = datetime.now(BerlinTz) formatted_time = current_time.strftime('%Y-%m-%d %H:%M:%S') await channel.send(f'{formatted_time}: Checking all cities ...') # 将耗时函数放入线程执行,不阻塞事件循环 response = await asyncio.to_thread(check_all) formatted_output = format_data(response) await channel.send(formatted_output)
兼容Python 3.8及以下版本的写法
用loop.run_in_executor手动提交任务到线程池:
import asyncio from datetime import datetime import discord from discord.ext import tasks @tasks.loop(minutes=5) async def send_message(): channel = discord.utils.get(client.get_all_channels(), name="bremen") current_time = datetime.now(BerlinTz) formatted_time = current_time.strftime('%Y-%m-%d %H:%M:%S') await channel.send(f'{formatted_time}: Checking all cities ...') # 获取事件循环,提交任务到默认线程池 loop = asyncio.get_event_loop() response = await loop.run_in_executor(None, check_all) formatted_output = format_data(response) await channel.send(formatted_output)
关键说明
- 之前用
asyncio.create_task无效的原因:如果check_all是同步阻塞函数,create_task只是把它加入事件循环的任务队列,依然会占用事件循环资源导致阻塞。只有放到线程池,才能真正解放事件循环。 - 确保
check_all是线程安全的:如果函数内有共享资源操作(比如全局变量、数据库连接),需要添加线程锁避免冲突。 - 该方案天然支持“执行完成后通知”:
await会等待线程中的函数执行完毕,自动获取结果后继续执行消息发送逻辑,无需额外的通知机制。
内容的提问来源于stack exchange,提问作者Ilyas
相关产品推荐
相关产品推荐

