Python - 在多处理调用之前修改的全局变量作为原始状态传递

时间:2017-03-17 15:38:05

标签: python multiprocessing

在Windows上的Python 3.5中,我试图设计一些多处理代码,这些代码需要一些预处理变量才能用于输入的函数。
为了使这些变量可用,我将它们视为全局变量。

虽然这是以非并行方法工作的,但使用multiprocessing.Pool会显示如果从初始化以来从未修改过全局会发生的行为。

请考虑以下代码段:

from multiprocessing import Pool

testlist = []


def f(x):
    return x*x + testlist[0]


def main():
    global testlist
    input_iter = range(10)
    testlist = [1, 2, 3, 4, 5]
    for i in input_iter:
        print(f(i))
    with Pool(2) as pool:
        for i in pool.imap_unordered(f, input_iter):
            print(i)

if __name__ == '__main__':
    main()

函数f(x)简单地对输入求平方,并添加全局变量testlist中的元素。 testlist首先全局定义为空列表,然后进行修改以包含[1, 2, 3, 4, 5]函数中的列表main()

运行此代码将为简单的for循环生成所需的输出,但多处理循环将向IndexError工作者抛出Pooltestlist变量尚未修改为包含值,仍然是一个空列表。

1
2
5
10
17
26
37
50
65
82
multiprocessing.pool.RemoteTraceback: 
"""
Traceback (most recent call last):
  File "\lib\multiprocessing\pool.py", line 119, in worker
    result = (True, func(*args, **kwds))
  File "progresstest.py", line 7, in f
    return x*x + testlist[0]
IndexError: list index out of range
"""

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "progresstest.py", line 21, in <module>
    main()
  File "progresstest.py", line 17, in main
    for i in pool.imap_unordered(f, input_iter):
  File "\lib\multiprocessing\pool.py", line 695, in next
    raise value
IndexError: list index out of range

在创建Pool个worker之前修改全局变量,而简单循环显示此赋值有效:IndexError循环中没有抛出for。我知道状态不能在进程之间共享,但是这个变量是在并行执行之前定义的,不会改变。

为什么会这样?

2 个答案:

答案 0 :(得分:2)

在类UNIX操作系统上,multiprocessing(默认情况下)使用fork系统调用创建Pool以制作一个或多个完全副本主过程。

在ms-windows上,没有fork系统调用,因此multiprocessing的工作方式不同。它启动一个新的Python进程,将原始程序作为模块导入。在这种情况下,系统不会调用main(),因此您的全局信息不会更新。

答案 1 :(得分:0)

我会将要传递的值包装在元组中(如Roland所建议的那样),或者包装在可能提供更灵活处理方式的列表中。这是带有(嵌套)列表的示例:

import multiprocessing
import os
from time import sleep
def foo(transptbox):
    [myvalues,a] = transptbox
    for j in myvalues:
        val1, val2, val3, val4, val5 = j
    a = a*a + val1   # stands for your process with iterable a and value 1
    sleep(0.1) # using sleep to simulate processing time long enough to activate multiprocessing
    print(f"Process-ID {os.getpid()}:{a} : {val1} {val2} {val3} {val4} {val5}") # just to show values are available
    return a

if __name__ == '__main__':
    values_to_be_sent =  [[1,2,3,4,5]]
    input_iter = range(10)
    tbox =[[0 for y in range(2)]for x in range(len(input_iter))]
    for x in range(len(input_iter)):
        tbox[x] = [values_to_be_sent, input_iter[x]]
    pool = multiprocessing.Pool(processes = 3)
    result = list(pool.map(foo, tbox))
    print(result)

输出是您想要的结果:

C:\Users\757\PycharmProjects\exu\venv\Scripts\python.exe C:/Users/757/.PyCharmCE2018.1/config/scratches/scratch_9.py
Process-ID 3476:1 : 1 2 3 4 5
Process-ID 4416:5 : 1 2 3 4 5
Process-ID 5568:2 : 1 2 3 4 5
Process-ID 3476:10 : 1 2 3 4 5
Process-ID 4416:17 : 1 2 3 4 5
Process-ID 5568:26 : 1 2 3 4 5
Process-ID 3476:37 : 1 2 3 4 5
Process-ID 5568:50 : 1 2 3 4 5
Process-ID 4416:65 : 1 2 3 4 5
Process-ID 3476:82 : 1 2 3 4 5
[1, 2, 5, 10, 17, 26, 37, 50, 65, 82]

Process finished with exit code 0