在Python多处理中同步写入共享内存(列表)

时间:2018-12-09 21:56:33

标签: python multiprocessing shared-memory python-multiprocessing multiprocessing-manager

我有以下代码:

import multiprocessing
manager = multiprocessing.Manager()

如果列表的长度小于4,则追加列表的函数,或创建一个初始值为“ y”的新列表。

def f(my_array):
    if len(my_array) < 4:
        my_array.append('x')
    else:
        my_array = ['y']
    print(my_array)

列表的初始化和创建过程。

if __name__ == '__main__':
    my_array = manager.list(['a', 'b', 'c'])

    p1 = Process(target=f, args=(my_array))
    p2 = Process(target=f, args=(my_array))
    p3 = Process(target=f, args=(my_array))
    p4 = Process(target=f, args=(my_array))
    p5 = Process(target=f, args=(my_array))

    p1.start()
    p2.start()
    p3.start()
    p4.start()
    p5.start()

    p1.join()
    p2.join()
    p3.join()
    p4.join()
    p5.join()

我得到的输出:

['a', 'b', 'c', 'x']
['y']
['y']
['y'] 
['y']

我不明白为什么列表仅被追加一次。我希望在第三条输出行中,我会观察到附有'x'的列表['y'],所以['y','x'],第四条['y','x','x']等等。似乎共享内存泄漏或不允许通过多个进程的功能进行更改。我该怎么做才能启用目标行为?

1 个答案:

答案 0 :(得分:1)

同步是您的示例中缺少的一点。 manager.list只是单独服务器进程中的常规list,您的工作进程可以通过代理对象进行修改。您的进一步流程碰巧同时检查了len(my_array)

没有同步,这告诉他们应该等到另一个进程完成其操作之后,该操作包括执行此长度检查并根据此检查结果执行操作。您的更新操作不是原子操作,您需要通过在操作周围使用manager.lock使其成为一体。

您的代码中还有另一个问题,即您重新绑定my_array以指向普通列表['y'],而不是更新/修改共享的manager.list。您没有使用设置manager.list的进程来修改my_array = ['y'],从第一次修改到第一个工作进程,manager.list保持其值['a', 'b', 'c', 'x']到程序结束。

from multiprocessing import Process, Manager


def f(my_array, lock):
    with lock:
        if len(my_array) < 4:
            my_array.append('x')
        else:
            my_array[:] = []  # clear list inplace by assigning
            # empty list to slice of manager.list
            my_array.append('y')
    print(my_array)


if __name__ == '__main__':

    N_WORKERS = 5

    with Manager() as manager:

        my_array = manager.list(['a', 'b', 'c'])
        lock = manager.Lock()

        pool = [
            Process(target=f, args=(my_array, lock)) for _ in range(N_WORKERS)
        ]

        for p in pool:
            p.start()
        for p in pool:
            p.join()

        # Leaving the context-manager block will shut down the manager-process.
        # We need to convert the manager-list to a normal list in the parent
        # to keep its values available for further processing in the parent.
        result = list(my_array)

    print(f'result: {result}')

示例输出:

['a', 'b', 'c', 'x']
['y']
['y', 'x']
['y', 'x', 'x']
['y', 'x', 'x', 'x']
result: ['y', 'x', 'x', 'x']

Process finished with exit code 0