如何并发获取Discord服务器多个频道的历史消息提升查询效率
问题根因
- 变量作用域问题:Python中字符串、整数属于不可变类型,将
msg和num作为参数传入starthistory时,函数内修改的只是局部副本,外部原始变量不会被更新。同时你没有等待asyncio.create_task创建的任务执行完成,主函数直接执行后续逻辑时遍历还未跑完,num自然保持初始值0。 - 串行执行耗时高:你用for循环逐个查询频道历史,每个查询都要等待上一个完成才能开始,服务器频道数量多的时候耗时会线性增长。
修复方案
核心是用asyncio.gather并发执行所有频道的查询任务,避免共享可变变量,直接收集符合条件的结果即可,同时修复原代码中embed.author读取的逻辑问题(discord.py中embed.author是对象,需要取name属性才能得到字符串):
import asyncio import textwrap # 抽离单个频道的最新消息检查逻辑,符合条件返回频道对象,不符合返回None async def check_channel(self, channel, key_lower): try: async for message in channel.history(limit=1): message_content = message.content.lower() if message.embeds: embed = message.embeds[0] if embed.title: message_content = embed.title.lower() elif embed.author and embed.author.name: message_content = embed.author.name.lower() elif embed.description: message_content = embed.description.lower() if message_content.startswith(key_lower): return channel except Exception: # 自动跳过无读取权限的频道 return None @_list.command(name="starts_with", aliases=["startswith", "sw", "s"], brief="Lists all channels with message starting with <key>.", help="Lists all channels with last message starting with the word/phrase <key>.", case_insensitive=True) async def _starts_with(self, ctx, *, key): key_lower = key.lower() wait = await ctx.send(f"Looking for messages starting with `{key}`...") # 为每个文本频道创建检查任务,并发执行 tasks = [self.check_channel(channel, key_lower) for channel in ctx.guild.text_channels] # 等待所有任务完成,收集结果 results = await asyncio.gather(*tasks) # 过滤得到符合条件的频道列表 matched_channels = [ch for ch in results if ch is not None] # 拼装结果消息 msg = f"Channels with last message starting with `{key}`:\n" if not matched_channels: msg += "**None**\n" else: for idx, channel in enumerate(matched_channels, start=1): msg += f"**{idx}.** {channel.mention} - **{channel.name}**\n" msg += f"\nTotal number of channels = **{len(matched_channels)}**" # 分段发送消息 for para in textwrap.wrap(msg, 2000, expand_tabs=False, replace_whitespace=False, fix_sentence_endings=False, break_long_words=False, drop_whitespace=False, break_on_hyphens=False, max_lines=None): await ctx.send(para) await asyncio.sleep(0.5) await wait.edit(content="✅ Done.")
优化说明
- 并发执行所有频道的查询请求,耗时从O(n)降低到接近单次查询的耗时,服务器频道越多性能提升越明显
- 去掉了全局共享的
msg、num变量,直接收集结果再拼装,避免了不可变类型参数传递的坑 - 增加了异常捕获,自动跳过没有读取权限的频道,避免命令直接报错
- 修复了原代码中直接读取
embed.author的逻辑错误
内容的提问来源于stack exchange,提问作者Witherr
相关产品推荐
相关产品推荐

