Discord.py Cogs/Tasks API疑问:为何__init__中启动的任务需手动启动?
Discord机器人定时任务在Cog初始化中无法启动的原因分析
问题描述
我在开发Discord机器人时尝试搭建定时任务功能,为排除外部变量干扰选择了定时循环任务。根据查阅的示例和文档,在Cog的__init__方法中调用self.printer.start()应该能启动任务,但实际运行时必须在bot的on_ready事件中手动启动任务才能正常运行,想知道这一现象的原因。
相关代码
MyCog.py
from discord.ext import commands, tasks from times import times class MyCog(commands.Cog): def __init__(self,bot): print("cog added") self.bot = bot bot.printer = self.printer self.index = 0 self.printer.start() print("end init") def cog_unload(self): self.printer.cancel() @tasks.loop(seconds=5.0) async def printer(self): print(self.index) self.index += 1 async def setup(bot): await bot.add_cog(MyCog(bot))
test.py
import datetime import discord from discord.ext import commands, tasks import asyncio from TOKEN import TOKEN from MyCog import MyCog intents = discord.Intents.default() intents.message_content = True bot = commands.Bot(command_prefix='!', intents=intents) @bot.event async def on_ready(): print(f'Logged in as {bot.user.name}') if not bot.printer.is_running(): bot.printer.start() #bot.MyCog.start() async def main(): await bot.add_cog(MyCog(bot=bot)) if __name__ == "__main__": asyncio.run(main()) bot.run(TOKEN)
运行环境:Python 3.10.4,Windows 10
原因分析
- 事件循环未初始化:discord.py的定时任务启动依赖bot内部的事件循环。在Cog的
__init__执行时,bot.run(TOKEN)还未调用,此时bot的核心事件循环尚未启动,调用start()只会将任务标记为待启动状态,无法真正进入循环执行。 - 独立事件循环冲突:代码中
asyncio.run(main())先执行,它会创建一个临时的事件循环来加载Cog,这个循环和后续bot.run()启动的bot专属事件循环完全独立。临时循环执行完add_cog就结束了,任务根本没有机会绑定到正确的bot循环上。而on_ready事件是在bot成功登录、内部循环完全启动后触发的,此时启动任务才能绑定到活跃的循环中。 - 任务绑定逻辑限制:
tasks.loop装饰的任务调用start()时,会自动绑定到当前线程的默认事件循环。在__init__阶段,要么没有活跃的循环,要么是临时的循环,和bot最终运行的循环不匹配,导致任务无法正常触发。
修正建议
方案1:使用Cog的cog_load方法启动任务
修改MyCog.py,将任务启动逻辑移到异步的cog_load方法中,该方法会在Cog加载到已启动的bot事件循环后执行:
from discord.ext import commands, tasks class MyCog(commands.Cog): def __init__(self,bot): print("cog added") self.bot = bot bot.printer = self.printer self.index = 0 print("end init") async def cog_load(self): self.printer.start() def cog_unload(self): self.printer.cancel() @tasks.loop(seconds=5.0) async def printer(self): print(self.index) self.index += 1 async def setup(bot): await bot.add_cog(MyCog(bot))
方案2:修正代码执行顺序
移除asyncio.run(main()),改用bot的扩展加载机制,确保Cog在bot事件循环启动后加载:
修改test.py:
import discord from discord.ext import commands from TOKEN import TOKEN intents = discord.Intents.default() intents.message_content = True bot = commands.Bot(command_prefix='!', intents=intents) @bot.event async def on_ready(): print(f'Logged in as {bot.user.name}') async def setup_hook(): await bot.load_extension("MyCog") bot.setup_hook = setup_hook if __name__ == "__main__": bot.run(TOKEN)
内容的提问来源于stack exchange,提问作者sntrenter
相关产品推荐
相关产品推荐

