多处理在python中写入数组的函数循环

时间:2017-01-12 11:18:38

标签: python arrays loops multiprocessing

我正在尝试为此循环实现多处理。它无法修改数组,或者似乎没有正确排序作业(在最后一个函数完成之前返回数组)。

import multiprocessing
import numpy


def func(i, array):
    array[i] = i**2
    print(i**2)

def main(n):
    array = numpy.zeros(n)

    if __name__ == '__main__':
        jobs = []
        for i in range(0, n):
            p = multiprocessing.Process(target=func, args=(i, array))
            jobs.append(p)
            p.start()

    return array

print(main(10))

2 个答案:

答案 0 :(得分:5)

进程不共享内存,程序最初将创建一个满零的数组,然后启动10个进程,这将在数组首次创建时调用func函数,但从不调用原始数组。< / p>

您似乎真正想要实现的目标是:

from multiprocessing import Process, Lock
from multiprocessing.sharedctypes import Array


def modify_array(index, sharedarray):
    sharedarray[index] = index ** 2
    print([x for x in sharedarray])


def main(n):
    lock = Lock()
    array = Array('i', 10, lock=lock)
    if __name__ == '__main__':
        for i in range(0, n):
            p = Process(target=modify_array, args=(i, array))
            p.start()
            p.join()
    return list(array)

main(10)

输出:

[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 1, 4, 0, 0, 0, 0, 0, 0, 0]
[0, 1, 4, 9, 0, 0, 0, 0, 0, 0]
[0, 1, 4, 9, 16, 0, 0, 0, 0, 0]
[0, 1, 4, 9, 16, 25, 0, 0, 0, 0]
[0, 1, 4, 9, 16, 25, 36, 0, 0, 0]
[0, 1, 4, 9, 16, 25, 36, 49, 0, 0]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 0]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

但问题是,使用多处理是错误的。与新线程相比,产生额外进程会产生大量开销,甚至只需保留单线程并利用事件循环来触发操作。

在Python的单线程单个进程中使用并发的示例可能如下所示:

import numpy as np
from asyncio import get_event_loop, wait, ensure_future


def modify_array(index, array):
    array[index] = index ** 2
    print([x for x in array])


async def task(loop, function, index, array):
    await loop.run_in_executor(None, function, index, array)


def main(n):
    loop = get_event_loop()
    jobs = list()
    array = np.zeros(10)
    for i in range(0, n):
        jobs.append(
            ensure_future(
                task(loop, modify_array, i, array)
            )
        )
    loop.run_until_complete(wait(jobs))
    loop.close()

main(10)

这是一种流行的模式,使用asyncio事件循环来并行完成任务。但是,由于您正在使用像Numpy这样的库,我质疑这种模式对您有多大价值。

答案 1 :(得分:3)

之前我没有使用multiprocessing所以我也是新手,但经过一些研究(主要来自these two帖子),我想我已经部分设法使用此代码解决您的问题:

import multiprocessing
import numpy


def func(i, array, connection):
    squared_value = i ** 2
    array[i] = squared_value
    print(squared_value)

    connection.send(array)


def main(n):
    array = numpy.zeros(n)

    for i in range(0, n):
        recv_end, send_end = multiprocessing.Pipe(False)
        p = multiprocessing.Process(target=func, args=(i, array, send_end))
        p.start()
        p.join()
        array = recv_end.recv()

    return array


if __name__ == '__main__':
    print(main(10))

<强>输出

0
1
4
9
16
25
36
49
64
81
[  0.   1.   4.   9.  16.  25.  36.  49.  64.  81.]

这种方法修改数组的原因并没有在this answer that I referenced in the comments中解释:

  

问题在于,当对象被传递给工作进程时,它们被打包并运送到另一个进程,在那里它们被解压缩并进行处理。您的对象不会像克隆那样传递给其他进程。您不会返回对象,因此克隆的对象会被愉快地修改,然后被丢弃。

关于我的(部分)解决方案,我应该指出一些事项:

  • 此实现运行很多比仅以常规方式生成此列表(通过单个线程)慢。这很可能是由于在它们之间创建新流程和编组数据所增加的开销。

  • 由于您的问题的性质(具有每个修改数组的作业),每个作业必须将上一个作业的输出作为输入。由于这种限制,我认为不可能同时运行作业,这种做法会使多处理失败。

至于后两点,我确实尝试了一种变体,其中func返回一个函数,它接受一个数组并返回它的修改版本。这将允许作业同时运行,但不幸的是,它似乎不能被腌制。