Python 多处理:在进程之间共享数据

Chr*_*per 3 python multiprocessing

我第一次尝试使用多处理并遇到一些相当基本的问题。我下面有一个玩具示例,其中两个进程正在将数据添加到列表中:

def add_process(all_nums_class, numbers_to_add):
    for number in numbers_to_add:
        all_nums_class.all_nums_list.append(number)

class AllNumsClass:
    def __init__(self):
        self.all_nums_list = []

all_nums_class = AllNumsClass()

p1 = Process(target=add_process, args=(all_nums_class, [1,3,5]))
p1.start()

p2 = Process(target=add_process, args=(all_nums_class, [2,4,6]))
p2.start()

all_nums_class.all_nums_list
Run Code Online (Sandbox Code Playgroud)

我希望在这些进程之间共享 all_nums_class ,以便它们都可以添加到其 all_nums_list - 所以结果应该是

[1,2,3,4,5,6]
Run Code Online (Sandbox Code Playgroud)

而不是我现在得到的,这只是旧的

[]
Run Code Online (Sandbox Code Playgroud)

有人可以请建议吗?我已经尝试了一下命名空间,但还没有让它在这里工作。

我觉得我最好提及(以防有所不同)我是在 Jupyter 笔记本上执行此操作。

nat*_*ncy 10

您可以使用多处理队列或管道在进程之间共享数据。队列是线程和进程安全的。使用管道时必须更加小心,因为如果两个进程(或线程)尝试同时从管道的同一端读取或写入,管道中的数据可能会损坏。当然,同时使用管道不同端的进程不存在损坏的风险。

目前,您的实现产生了两个独立的进程,每个进程都有自己的 self.all_nums_list. 因此,您实际上生成了 的三个对象AllNumsClass:一个在主程序中,一个在 中p1,一个在 中p2。由于进程是独立的并且不共享相同的内存空间,因此它们会正确附加self.all_nums_list,但每个进程都会附加到自己的附加空间。这就是为什么当您all_nums_class.all_nums_list在主程序中打印时,您正在打印主进程,self.all_nums_list这是一个空列表。要共享数据并将进程附加到同一列表,我建议使用队列。

使用队列和进程的示例

import multiprocessing as mp

def add_process(queue, numbers_to_add):
    for number in numbers_to_add:
        queue.put(number)

class AllNumsClass:
    def __init__(self):
        self.queue = mp.Queue()
    def get_queue(self):
        return self.queue

if __name__ == '__main__':
    
    all_nums_class = AllNumsClass()

    processes = []
    p1 = mp.Process(target=add_process, args=(all_nums_class.get_queue(), [1,3,5]))
    p2 = mp.Process(target=add_process, args=(all_nums_class.get_queue(), [2,4,6]))

    processes.append(p1)
    processes.append(p2)
    for p in processes:
        p.start()
    for p in processes:
        p.join()

    output = [] 
    while all_nums_class.get_queue().qsize() > 0:
        output.append(all_nums_class.get_queue().get())
    print(output)
Run Code Online (Sandbox Code Playgroud)

此实现是异步的,因为它不按顺序应用。每次运行它时,您可能会得到不同的输出。

输出示例

[1,2,3,5,4,6]

[1,3,5,2,4,6]

[2,4,6,1,3,5]

[2,1,4,3,5,6]

维护有序或无序结果列表的一种更简单的方法是使用mp.Pool类。具体来说,Pool.apply和 的Pool.apply_async功能。Pool.apply将锁定主程序直到所有进程完成,如果我们想为某些应用程序以特定顺序获取结果,这非常有用。相反,Pool.apply_async将立即提交所有进程并在完成后立即检索结果。另一个区别是我们需要get在调用后使用该方法Pool.apply_async才能获取已完成进程的返回值。