Uncategorized · 12月 10, 2021 0

Python多进程模块multiprocessing使用雷区

相比于多线程,Python使用多进程的好处就不用多说了,总之一般认识上比多线程跑得快就完事儿了。但能力越大责任越大,多进程的雷区也比较多。这篇文章主要用来排那些咱在实际操作中遇到的雷。

1. OSError: [WinError 87] The parameter is incorrect

在Windows中执行使用了多进程的程序,有时会报这样的错:

Traceback (most recent call last):
...
TypeError: can't pickle _thread.lock objects
...
OSError: [WinError 87] The parameter is incorrect

注意到那行”can’t pickle _thread.lock objects”了吗?一般多进程的定义写成如下形式:

p1 = multiprocessing.Process(target=func, args=(), ...)

Windows下执行多进程时要求给多进程定义传递的参数(即Process()里的参数)都是可pickle的。也就是说执行pickle.dumps(参数)不会报错。一般变量、类、函数啥的一般都可以pickle。那什么不可以呢?闭包不可以。我发现自己写的程序里出现了这样的操作:

# 略略略
def func1():
    def _func2(x, y):
        # 略略略
    # 略略略
    p1 = multiprocessing.Process(target=_func2, args=(x, y, ))
    p1.start()
    # 略略略

在这里_func2就是不可pickle的。就会报错了。

2. 用PyInstaller打包多进程Python代码的麻烦

现在程序不报错了,跑起来了。你准备用PyInstall将代码打包成EXE分发给小伙伴。
打包完成,分发众人。忽闻卧槽,望风而去。之前跑得好端端的程序打了包怎么就报错了呢?看了一眼报错内容:

Traceback (most recent call last):
  File "xxx.py", line 61, in <module>
ValueError: invalid literal for int() with base 10: 'pipe_handle=624'
[26420] Failed to execute script 'xxx' due to unhandled exception!
Traceback (most recent call last):
  File "xxx.py", line 61, in <module>
ValueError: invalid literal for int() with base 10: 'pipe_handle=632'
[25752] Failed to execute script 'xxx' due to unhandled exception!

绝了,还每个进程报一个错。查了一下,需要在if __name__ == '__main__':的下面加这么一行:

# 略略略
if __name__ == '__main__':
    multiprocessing.freeze_support()  # 注意位置。加在代码开头一样会报错哦
    # 略略略

这样多进程本身应该不会再造成error了。剩下的就看你写的代码的造化了。

3. 关于获取多进程执行函数的返回值

干过这种事吗👇

def func(c):
    # 略略略
    c.insert(返回值)

c = Container()  # 名字瞎起的。里面可能有列表之类的变量
p1 = multiprocessing.Process(target=_func2, args=(c, ))
p1.start()

别这么干了,没用的。c里空空如也。