繁体   English   中英

Python 使用列表理解的多处理问题

[英]Python multiprocessing problem using list comprehension

我无法理解为什么不更新多处理、列表理解输入,特别是下面示例代码中的“val2chg”条目。 我不会对此感到惊讶,除非它在没有多处理的情况下工作正常。 如果在没有列表理解的情况下传递“相同”列表,则多处理代码是成功的。 我敢肯定其他人也遇到过这个问题,但我无法正确地表达我的搜索词以提供指向答案的链接。

下面是一个简单的例子,结果后跟代码。

The input (sld) is
   [{'val': 0, 'val2chg': 'not'}, {'val': 1, 'val2chg': 'not'}]
The four results are
   multiproc: True. listcomp: True, [{'val': 0, 'val2chg': 'not'}, {'val': 1, 'val2chg': 'not'}]
   multiproc: False. listcomp: True, [{'val': 0, 'val2chg': 'changed'}, {'val': 1, 'val2chg': 'changed'}]
   multiproc: False. listcomp: False, [{'val': 0, 'val2chg': 'changed'}, {'val': 1, 'val2chg': 'changed'}]
   multiproc: True. listcomp: False, [{'val': 0, 'val2chg': 'changed'}, {'val': 1, 'val2chg': 'changed'}]

import multiprocessing as mp
sld = [{'val':0, 'val2chg':'not'}, {'val':1, 'val2chg':'not'}]
print(sld)
def update_values(sld, domp):
        global update
        def update(idx, some):
                some['val2chg'] = 'changed'
                return idx, some
        if domp:
                pool = mp.Pool()
                results = [pool.apply_async(update, (idx, sld[idx])) for idx in range(len(sld))]
                pool.close()
                pool.join()
                for result in results:
                        idx = result.get()[0]
                        sld[idx] = result.get()[1]
        else:
                for idx in range(len(sld)):
                        results = update(idx, sld[idx])
                        idx = results[0]
                        sld[idx] = results[1]
domp = True
listcomp = False
if listcomp:
        update_values([s for idx, s in enumerate(sld)], domp)
else:
        update_values(sld, domp)
print(f'multiproc: {domp}. listcomp: {listcomp}, {sld}')

我将尽力解释这一点:

首先你的声明:

if listcomp:
        update_values([s for idx, s in enumerate(sld)], domp)

可以简化为:

if listcomp:
        update_values([s for s in sld)], domp)

这相当于:

if listcomp:
        arg = [s for s in sld)]
        update_values(arg, domp)

但是您现在要做的是向update_values传递一个列表arg ,它是列表sld的浅表副本,而不是sld本身,其中arg[0] == sld[0] ,即每个列表都引用相同的字典元素。

在调用update的非多处理情况下,您将传递对argsld的每个元素的引用,具体取决于domp的值。 但在任何一种情况下,它们都是sld中包含的相同字典引用。 因此update中的代码some['val2chg'] = 'changed'实际上是在原地更新sld引用的字典(同样,它是arg引用的相同字典)。 因此,即使listcomp为 true 并且update的返回值被用于更新过去的arg列表而不是sld列表,也没关系,因为sld引用的字典元素已经由update

但是,在多处理情况下,现在传递给update的是字典引用的序列化/反序列化副本 语句some['val2chg'] = 'changed'正在修改该副本,该副本位于执行该语句的池进程的地址空间中。 所以我们现在必须依靠update_values使用 update 的返回值来update传递的sld参数,不幸的是,它不是全局sld列表,而是它的副本arg

以下是更新的代码,与您的代码不同,它仅适用于默认情况下使用fork方法创建新进程的平台(例如 Linux),应该适用于所有平台。 我还修改了间距以符合 PEP8 指南:

import multiprocessing as mp

def update(idx, some):
    some['val2chg'] = 'changed'
    return idx, some

def update_values(sld, domp):
    if domp:
        pool = mp.Pool()
        results = [pool.apply_async(update, (idx, sld[idx])) for idx in range(len(sld))]
        pool.close()
        pool.join()
        for result in results:
            idx, some = result.get()
            sld[idx] = some
    else:
        for idx in range(len(sld)):
            results = update(idx, sld[idx])
            idx = results[0]
            sld[idx] = results[1]

if __name__ == '__main__':
    domp = True
    listcomp = True

    sld = [{'val':0, 'val2chg':'not'}, {'val':1, 'val2chg':'not'}]
    print(sld)

    if listcomp:
        #arg = [s for idx, s in enumerate(sld)]
        # The above can be simplified to:
        arg = [s for s in sld]
        # But now arg is a copy of sld and it is the copy that is being modified
        update_values(arg, domp)
        # We now need this (but we really shouldn't be using a list comprehension at all):
        sld = arg
    else:
        update_values(sld, domp)
    print(f'multiproc: {domp}. listcomp: {listcomp}, {sld}')

印刷:

[{'val': 0, 'val2chg': 'not'}, {'val': 1, 'val2chg': 'not'}]
multiproc: True. listcomp: True, [{'val': 0, 'val2chg': 'changed'}, {'val': 1, 'val2chg': 'changed'}]

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM