非rewrite版discord.py如何创建可并行运行的多循环后台任务?
核心问题定位
当前代码的阻塞根源来自start_tasks方法的任务创建逻辑错误:
- 你在列表推导式中对
self.client.loop.create_task()的返回结果执行了await操作 - 对于非rewrite版本的discord.py配套的asyncio版本,
loop.create_task是同步方法:它会将传入的协程封装为可调度的Future并立刻返回,本身不需要被await;如果你对返回的Future直接await,代码逻辑会停在这一步,直到当前对应的任务完全执行结束,才会进入下一个任务的创建流程 - 结合
task_1内置的60分钟asyncio.sleep长阻塞,后续的task_2、task_3根本没有被创建的机会,最终表现为只有第一个启动的任务在运行,其余任务全部被阻塞
可并行运行的修正实现
import asyncio import discord class BackgroundTasksCollection: def __init__(self, client: discord.Client): self.client = client # 存储已启动的任务句柄,避免on_ready重复触发导致任务堆叠 self.running_tasks = set() async def start_tasks(self): # 已存在运行中的任务时直接退出,防止重复创建 if self.running_tasks: return for name in dir(self): if name.startswith("task_"): task_coro = getattr(self, name)() # 同步创建任务,不等待任务执行完成,让事件循环自动调度所有任务并行运行 task_handle = self.client.loop.create_task(task_coro) self.running_tasks.add(task_handle) # 增加回调,将已结束的任务从集合中移除,避免内存泄漏 task_handle.add_done_callback(self.running_tasks.discard) # 以下原有任务逻辑无需修改 async def task_1(self): await self.client.wait_until_ready() while not self.client.is_closed(): await asyncio.sleep(60 * 60) # 原有业务逻辑 async def task_2(self): await self.client.wait_until_ready() while not self.client.is_closed(): await asyncio.sleep(1) # 原有业务逻辑 async def task_3(self): await self.client.wait_until_ready() while not self.client.is_closed(): await asyncio.sleep(30) # 原有业务逻辑 class MyClient(discord.Client): def __init__(self, **options): super().__init__(**options) self.tasks = BackgroundTasksCollection(self) async def on_ready(self): await self.tasks.start_tasks() # 原有on_ready逻辑
常见坑点提醒
- 所有任务内的等待逻辑统一使用
asyncio.sleep,严禁使用同步的time.sleep,同步阻塞会卡住整个事件循环,导致所有机器人交互、后台任务全部停摆 - 每个任务内部建议增加基础异常捕获,避免单个任务抛出未捕获的异常被事件循环静默吞掉,提升排查效率
- 不需要额外使用线程来实现后台并行,asyncio的单线程事件循环本身就能调度大量协程并行轮转运行,配合非阻塞的sleep逻辑即可实现所有任务同时正常运行
内容的提问来源于stack exchange,提问作者Kirk KD
相关产品推荐
相关产品推荐

