天天看点

python中的multiprocessing(windows)

multiprocessing是一个多进程库,在linux环境下和windows环境下都可以使用,是一个跨平台多进程模块,但是背后运行机制会稍有不同,本文重点放在windows环境下的使用。

必须在顶层脚本文件中使用

使用multiprocessing中的多进程必须在顶层脚本文件中使用,且需要使用if __name__==’__main__’:语句,否则将会导致循环调用抛出异常。

# 本文件名为multi_process_test.py
import multiprocessing
import os

print(__name__)

def print_log():
    print('Current pid is %s' % os.getpid())


if __name__=='__main__':
    print('main pid is %s' % os.getpid())
    p = multiprocessing.Process(target=print_log)
    p.start() # 进程开始运行
    p.join()
           

输出结果:

__main__
main pid is 248
__mp_main__
Current pid is 20748
           

结果分析:

从结果可以看出,子进程开始运行之后,相当于在命令行环境中输入python multi_process_test.py,但是__name__会从__main__变为__mp__main__,其余和主进程没有任何区别,这也是为什么必须在if __name__==’__main__’:语句调用多进程的原因,否则会导致无限循环调用多进程。

Process和pickle的关系

通过上面的分析,当子进程代码执行到if __name__==’__main__’:语句时,由于__name__==’__mp__main__'后没有代码继续执行了,难道子进程啥也不干就退出了吗?当然不会。 multiprocessing.Process()函数会把放入子进程的函数所需要的参数通过pickle保存起来,当子进程没有代码可执行后,就会把这些参数重新unpickle出来然后放入函数中运算。

这样一看多进程也不过如此嘛,洒洒水啦,但是后来测试发现并不是所有对象都是pickleable的(比如open()返回的文件对象)。

import multiprocessing
import os

print(__name__)


def write_log(file):
    print('Current pid is %s' % os.getpid())
    file.write('*' * 20 + '\n')
    file.close()

file = open('log.txt', 'w')

if __name__ == '__main__':
    print('main pid is %s' % os.getpid())
    p = multiprocessing.Process(target=write_log, args=(file,))
    p.start()
    p.join()
           

输出结果:

__main__
main pid is 17416
Traceback (most recent call last):
  File "D:/Project/SciOCR-PyCharm/test.py", line 17, in <module>
    p.start()
  File "D:\ANACONDA\envs\py3.5\lib\multiprocessing\process.py", line 105, in start
    self._popen = self._Popen(self)
  File "D:\ANACONDA\envs\py3.5\lib\multiprocessing\context.py", line 212, in _Popen
    return _default_context.get_context().Process._Popen(process_obj)
  File "D:\ANACONDA\envs\py3.5\lib\multiprocessing\context.py", line 313, in _Popen
    return Popen(process_obj)
  File "D:\ANACONDA\envs\py3.5\lib\multiprocessing\popen_spawn_win32.py", line 66, in __init__
    reduction.dump(process_obj, to_child)
  File "D:\ANACONDA\envs\py3.5\lib\multiprocessing\reduction.py", line 59, in dump
    ForkingPickler(file, protocol).dump(obj)
TypeError: cannot serialize '_io.TextIOWrapper' object
           

这可怎么办,可难死我了,只见一道闪电从天空划过,我脑海中浮现了一个大胆的想法,不传参数进去不就好了。

import multiprocessing
import os

print(__name__)
args = None

def write_log():
    global args
    print('Current pid is %s' % os.getpid())
    args.write('*' * 20 + '\n')
    args.close()

file = open('log.txt', 'w')
args = file

if __name__ == '__main__':
    print('main pid is %s' % os.getpid())
    p = multiprocessing.Process(target=write_log)
    p.start()
    p.join()
           

这个方法我主要是用来针对另一种稍微不同情况的用法,所以这里这个例子有点牵强,仅供参考,给大家提供一个思路。

参考资料:

python 3.4 multiprocessing does not work with unittest

python pickle模块

多进程 廖雪峰