从多处理收集结果时确保正确的顺序

时间:2014-02-26 10:15:24

标签: python python-2.7 dictionary multiprocessing

我有一个多处理脚本循环遍历字典,如下所示:

jobs = []
for key, val in datadict.items():
    jobs.append(pool.apply_async(worker, (val))

pool.close()
pool.join()

jobs是结果对象的列表,(调用get()将提供数据列表)

我想格式化结果,使它们是一个字典,其键和顺序与输入字典相同。

我想在完成所有工作后立即执行此操作:

result = {key: jobs[key].get() for key, val in datadict} 

这是有效的,因为datadict中的键是整数(因此可用于索引作业列表)。 但后来我发现,也许最终的工作清单不一定是相同的顺序(因为它是创造的) - 这是真的吗? (我希望订单可能变得混乱,因为一个过程可能比另一个过程更快完成等)

所以我决定将key datadict传递给worker函数,然后再将其返回,结果为元组。因此调用jobs[index].get()将返回一个元组,其中第一个值是键(刚刚通过函数),第二个值是实际结果

然后我可以创建一个字典:

result = dict([job.get() for job in jobs])

所以最终的脚本是:

def worker(val, key):        
    res = "Do something to val"
    return (key, res)

if __name__ == "__main__":
    jobs = []
    for key, val in datadict.items():
        jobs.append(pool.apply_async(worker, (val, key))

    pool.close()
    pool.join()

    result = dict([job.get() for job in jobs])

但这是最好的方法吗?有几点让我烦恼:

  1. 到目前为止,jobs列表的结果顺序与输入顺序
  2. 相匹配
  3. 传递一个值'通过'一个函数(即对它无所作为)似乎有点愚蠢

3 个答案:

答案 0 :(得分:2)

显式排序datadict字典键,并迭代它。

import multiprocessing

def worker(val):
    res = "Do something to val {}".format(val)
    return res

if __name__ == "__main__":
    datadict = {1: 'val1', 2: 'val2', 0: 'val0'}
    jobs = []
    pool = multiprocessing.Pool()
    for key in sorted(datadict): # <------------
        jobs.append(pool.apply_async(worker, (datadict[key],)))
    pool.close()
    pool.join()
    result = [job.get() for job in jobs]
    print(result)
    # ['Do something to val 0', 'Do something to val 1', 'Do something to val 2']

顺便说一句,如果worker只接受一个参数,则可以使用Pool.map

if __name__ == "__main__":
    datadict = {1: 'val1', 2: 'val2', 0: 'val0'}
    jobs = []
    pool = multiprocessing.Pool()
    result = pool.map(worker, sorted(datadict)) # <---
    pool.close()
    pool.join()

答案 1 :(得分:1)

dict容器不保证任何特定排序。如果您想保留订单,则需要将结果存储在list

result = [job.get() for job in jobs]

或者您可以使用OrderedDict来维护广告订单:

result = OrderedDict([job.key, job.get() for job in jobs])

第二种解决方案需要一种从作业中获取密钥的方法。

<强>更新

如果通过密钥给出订单,那么您只需按此属性对结果进行排序(作业需要知道其密钥):

results = [job.get() for job in jobs]
results = sorted(results, key=attrgetter('key'))

或者如果您需要dict

results = [job.get() for job in jobs]
results = OrderedDict([job.key, job for job in sorted(results, key=attrgetter('key'))])

答案 2 :(得分:0)

作业可能无序完成,但这不会改变jobs列表的顺序。但是,您可以通过循环jobs来填充datadict.items();这使得它们处于任意顺序,因为字典不能保持秩序。

将密钥放在jobs中将是实现您想要的一种方式:

jobs = []
for key, val in datadict.items():
    jobs.append((key, pool.apply_async(worker, (val,)))

pool.close()
pool.join()

result = {key: job.get() for key, job in jobs}