如何让 Python 在多处理时尊重可迭代字段?

Sku*_*der 4 python iterable multiprocessing non-static concurrent.futures

如果这是一个愚蠢的问题,我深表歉意,但我还没有找到解决此问题的优雅解决方法。基本上,当使用 concurent.futures 模块时,类的非静态方法看起来应该可以正常工作,我在模块的文档中没有看到任何表明它们无法正常工作的内容,并且该模块不会产生任何结果运行时出现错误 - 在许多情况下甚至会产生预期的结果!

但是,我注意到该模块似乎不尊重在父线程中进行的可迭代字段的更新,即使这些更新发生在启动任何子进程之前也是如此。这是我的意思的一个例子:

import concurrent.futures


class Thing:
    data_list = [0, 0, 0]
    data_number = 0

    def foo(self, num):
        return sum(self.data_list) * num

    def bar(self, num):
        return num * self.data_number


if __name__ == '__main__':
    thing = Thing()
    thing.data_list[0] = 1
    thing.data_number = 1

    with concurrent.futures.ProcessPoolExecutor() as executor:
        results = executor.map(thing.foo, range(3))
        print('result of changing list:')
        for result in results:
            print(result)

        results = executor.map(thing.bar, range(3))
        print('result of changing number:')
        for result in results:
            print(result)
Run Code Online (Sandbox Code Playgroud)

我希望这里的结果是

result of changing list:
0
1
2
result of changing number:
0
1
2
Run Code Online (Sandbox Code Playgroud)

但我得到

result of changing list:
0
0
0
result of changing number:
0
1
2
Run Code Online (Sandbox Code Playgroud)

因此,出于某种原因,对于整数字段,事情按预期工作,但对于列表字段,事情根本不按预期工作。这意味着,当调用子进程时,不会考虑对列表所做的更新,即使对较简单字段的更新会被考虑。我也用字典尝试过同样的问题,我怀疑这对所有可迭代都是一个问题。

有没有什么方法可以使其按预期工作,从而允许子进程尊重可迭代字段的更新?非静态方法的多重处理会像这样半实现,这似乎很奇怪,但我希望我只是错过了一些东西!

Boo*_*boo 5

这个问题与“尊重可迭代字段”无关,但这是一个相当微妙的问题。在您的主要流程中,您有:

thing.data_list[0] = 1 # first assignment
thing.data_number = 1 # second assignmment
Run Code Online (Sandbox Code Playgroud)

而不是:

Thing.data_list[0] = 1 # first assignment
Thing.data_number = 1 # second assignment
Run Code Online (Sandbox Code Playgroud)

就第一个分配而言,没有任何实质性差异,因为对于任一版本,您都不会修改类属性,而是修改列表中恰好被类属性引用的元素。换句话说,Thing.data_list仍然指向同一个列表;该参考没有改变。这是一个重要的区别。

但在您的代码版本的第二次分配中,您实际上已经通过实例的self引用修改了类属性。当您这样做时,您将创建一个具有相同名称data_number的新实例属性

您的类成员起作用foobar尝试通过 访问类属性self。该Thing实例thing将被pickle到新的地址空间,但在新的地址空间中,当Thingun -pickled时,默认情况下将创建新的类属性并将其初始化为其默认值,除非您添加特殊的 pickle 规则。但是实例属性应该成功传输,例如您新创建的data_number. 这就是为什么“更改数字的结果:”按照您的预期打印出来,即您实际上正在访问中的实例属性。data_numberbar

更改bar为以下内容,您将看到所有内容都会打印为0

    def bar(self, num):
        return num * Thing.data_number
Run Code Online (Sandbox Code Playgroud)