You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 17:22:54