我知道multiprocessing.Manager()
以及它如何用于创建共享对象,特别是可以在worker之间共享的队列。有this question,this question,this 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,)
我没有使用pool
和manager
,而是使用以下内容启动流程:
mp.Process(target=targ1, args=(DICT[key],))
3 - 函数targ1
获取来自主进程的输入数据(按key
排序)。它旨在将结果传递给DICT[key]
,以便targ2
可以完成其工作。这是不起作用的部分。存在任意数量的targ1
s,targ2
s等,因此具有任意数量的队列。
4 - 其中一些进程的结果将被发送到一堆不同的数组/ pandas数据帧,这些数据帧也被key
索引,我希望可以从任意进程访问它们,甚至是已启动的进程在另一个模块中。我还没有写这部分,这可能是一个不同的问题。 (我在这里提到它,因为上面3的答案也可能很好地解决了。)
答案 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
将使用的队列的引用。如果你确实有“任意数量”的队列,我肯定会建议尽可能快地收集旧队列。