在Python中的进程之间共享许多队列

时间:2015-04-30 14:12:29

标签: python queue multiprocessing python-multiprocessing

我知道multiprocessing.Manager()以及它如何用于创建共享对象,特别是可以在worker之间共享的队列。有this questionthis questionthis question甚至one of my own questions

但是,我需要定义很多队列,每个队列都链接一对特定的进程。假设每对进程及其链接队列由变量key标识。

当我需要输入和获取数据时,我想使用字典来访问我的队列。我无法做到这一点。我尝试过很多东西。将multiprocessing导入mp

在多处理模块(称为for key in all_keys: DICT[key] = mp.Queue)导入的配置文件中定义类似multi.py的dict不会返回错误,但队列DICT[key]之间不会共享进程中,每个进程似乎都有自己的队列副本,因此不会发生通信。

如果我尝试在主多处理函数的开头定义DICT来定义进程并启动它们,比如

DICT = mp.Manager().dict()    
for key in all_keys:
    DICT[key] = mp.Queue()

我收到错误

RuntimeError: Queue objects should only be shared between processes through
 inheritance

更改为

DICT = mp.Manager().dict()    
for key in all_keys:
    DICT[key] = mp.Manager().Queue()

只会让一切变得更糟。在multi.py的头部而不是在main函数内部尝试类似的定义会返回类似的错误。

必须有一种方法可以在进程之间共享许多队列,而无需在代码中明确命名每个队列。有什么想法吗?

修改

以下是该计划的基本架构:

1-加载第一个模块,它定义了一些变量,导入multi,启动multi.main(),并加载另一个启动模块加载和代码执行的模块。同时......

2- multi.main看起来像这样:

def main():
    manager = mp.Manager()
    pool = mp.Pool()
    DICT2 = manager.dict()

    for key in all_keys:
        DICT2[key] = manager.Queue()
        proc_1 = pool.apply_async(targ1,(DICT1[key],) ) #DICT1 is defined in the config file
        proc_2 =  pool.apply_async(targ2,(DICT2[key], otherargs,) 

我没有使用poolmanager,而是使用以下内容启动流程:

mp.Process(target=targ1, args=(DICT[key],))

3 - 函数targ1获取来自主进程的输入数据(按key排序)。它旨在将结果传递给DICT[key],以便targ2可以完成其工作。这是不起作用的部分。存在任意数量的targ1 s,targ2 s等,因此具有任意数量的队列。

4 - 其中一些进程的结果将被发送到一堆不同的数组/ pandas数据帧,这些数据帧也被key索引,我希望可以从任意进程访问它们,甚至是已启动的进程在另一个模块中。我还没有写这部分,这可能是一个不同的问题。 (我在这里提到它,因为上面3的答案也可能很好地解决了。)

1 个答案:

答案 0 :(得分:20)

当您尝试通过将其作为参数传递来共享multiprocessing.Queue()时,听起来您的问题已经开始了。您可以通过创建managed queue来解决此问题:

import multiprocessing
manager = multiprocessing.Manager()
passable_queue = manager.Queue()

当您使用管理器创建它时,您正在存储并将代理传递给队列,而不是队列本身,因此即使传递给工作进程的对象是复制后,它仍将指向相同的底层数据结构:您的队列。它与C / C ++中的指针非常相似(在概念上)。如果以这种方式创建队列,则可以在启动工作进程时传递它们。

由于您现在可以传递队列,因此您不再需要管理字典。在main中保存一个普通字典,用于存储所有映射,并且只为您的工作进程提供所需的队列,这样他们就不需要访问任何映射。

我在这里写了一个例子。看起来你正在你的工人之间传递物品,所以这就是在这里做的。想象一下,我们有两个处理阶段,数据的开始和结束都在main的控制之中。看看我们如何创建连接工作人员的队列,就像管道一样,但只要给他们他们需要的队列,他们就不需要知道任何映射:

import multiprocessing as mp

def stage1(q_in, q_out):

    q_out.put(q_in.get()+"Stage 1 did some work.\n")
    return

def stage2(q_in, q_out):

    q_out.put(q_in.get()+"Stage 2 did some work.\n")
    return

def main():

    pool = mp.Pool()
    manager = mp.Manager()

    # create managed queues
    q_main_to_s1 = manager.Queue()
    q_s1_to_s2 = manager.Queue()
    q_s2_to_main = manager.Queue()

    # launch workers, passing them the queues they need
    results_s1 = pool.apply_async(stage1, (q_main_to_s1, q_s1_to_s2))
    results_s2 = pool.apply_async(stage2, (q_s1_to_s2, q_s2_to_main))

    # Send a message into the pipeline
    q_main_to_s1.put("Main started the job.\n")

    # Wait for work to complete
    print(q_s2_to_main.get()+"Main finished the job.")

    pool.close()
    pool.join()

    return

if __name__ == "__main__":
    main()

代码生成此输出:

  

主要开始这项工作   第一阶段做了一些工作   第二阶段做了一些工作   主要完成了这项工作。

我没有包含在字典中存储队列或AsyncResults对象的示例,因为我仍然不太了解您的程序应该如何工作。但是现在您可以自由地传递队列,您可以构建字典以根据需要存储队列/进程映射。

事实上,如果你真的在多个工作者之间建立管道,你甚至不需要在main中保留对“工人间”队列的引用。创建队列,将它们传递给您的工作人员,然后仅保留对main将使用的队列的引用。如果你确实有“任意数量”的队列,我肯定会建议尽可能快地收集旧队列。