discord.py on_message中多进程执行for loop报错求助
问题:discord.py中使用multiprocessing执行内部函数报错
我首次用multiprocessing模块,想在discord.py的on_message函数里用多进程加速处理for循环,但调用process.start()时触发两个错误:
AttributeError: Can't pickle local object 'main.<locals>.scrapeall'OSError: [WinError 6] 句柄无效
代码里把scrapeall函数定义在main()内部,在on_message事件里创建多个Process实例执行它。scrapename()负责爬取谷歌页面,需要靠多进程提升循环效率,目标是实现这个多任务功能。
原代码片段:
def main(): def scrapeall(unit, idx, name, nom, sk, photo): if Counter(name[:idx+1])[unit]==1: nom[idx]=scrapename(unit) if nom[idx].startswith("BUY"): skname="" urlimg="" else: with open("[...]/historic.csv","r",newline='') as incsv: [...] if Counter(name[:idx+1])[unit]>1: indexfirst=name.index(unit) nom[idx]=nom[indexfirst] skname=sku[indexfirst] urlimg=photo[indexfirst] sk[idx]=skname photo[idx]=urlimg @bot.event async def on_message(message): await bot.wait_until_ready() await bot.process_commands(message) [...] processes= [Process(target=scrapeall, args=(unit, idx, name, nom, sk, photo)) for idx,unit in enumerate(name)] for process in processes: process.start() for process in processes: process.join() bot.run(TOKEN) if __name__ == '__main__': main()
解决方案
1. 把scrapeall移到全局作用域
Python多进程需要序列化(pickle)目标函数,但局部函数无法被pickle,这是第一个错误的核心原因。把scrapeall移到main()外面就能解决:
修改后的代码结构:
def scrapeall(unit, idx, name, nom, sk, photo): # 原函数逻辑完全保留 if Counter(name[:idx+1])[unit]==1: nom[idx]=scrapename(unit) if nom[idx].startswith("BUY"): skname="" urlimg="" else: with open("[...]/historic.csv","r",newline='') as incsv: [...] if Counter(name[:idx+1])[unit]>1: indexfirst=name.index(unit) nom[idx]=nom[indexfirst] skname=sku[indexfirst] urlimg=photo[indexfirst] sk[idx]=skname photo[idx]=urlimg def main(): @bot.event async def on_message(message): await bot.wait_until_ready() await bot.process_commands(message) [...] processes= [Process(target=scrapeall, args=(unit, idx, name, nom, sk, photo)) for idx,unit in enumerate(name)] for process in processes: process.start() for process in processes: process.join() bot.run(TOKEN) if __name__ == '__main__': main()
2. 确保传递给子进程的参数可序列化
WinError 6一般是因为传给子进程的参数无法被正确序列化,比如discord.py的对象、自定义不可pickle的容器。要保证args里的所有参数都是可pickle的:
- 如果
name、nom、sk、photo是普通列表/字典,没问题;如果是自定义对象,要确保它支持pickle - 别直接把discord的
message对象或bot实例传给子进程,只提取需要的字符串、数值等简单类型
3. 用multiprocessing.Pool简化多进程逻辑
如果只是并行处理循环任务,用Pool会更省心,还能自动控制进程数量:
from multiprocessing import Pool # 在on_message事件里替换原进程创建代码 async def on_message(message): # ... 其他逻辑 ... # 整理参数列表,每个元素是scrapeall需要的参数元组 args_list = [(unit, idx, name, nom, sk, photo) for idx,unit in enumerate(name)] with Pool(processes=4) as pool: # 进程数可根据CPU核心数调整 pool.starmap(scrapeall, args_list)
4. 注意discord.py的异步环境
子进程独立于主进程的事件循环,不要在子进程里调用discord.py的异步API(比如发送消息)。如果需要把结果同步到discord,应该在主进程收集子进程的返回值后再操作。
内容的提问来源于stack exchange,提问作者Fabar
相关产品推荐
相关产品推荐

