将管道/连接作为上下文arg传递给多处理Pool.apply_async()

时间:2013-11-19 19:40:38

标签: python multiprocessing ipc pipe

我想使用管道与池中的流程实例进行通信,但是我收到错误:

让__p成为Pool()的实例:

    (master_pipe, worker_pipe) = Pipe()

    self.__p.apply_async(_worker_task, 
                         (handler_info, 
                          context_info,
                          worker_pipe))

当我执行此操作时,我得到以下错误[对于每个实例,显然]:

  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/multiprocessing/queues.py", line 376, in get
    task = get()
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/multiprocessing/queues.py", line 376, in get
TypeError: Required argument 'handle' (pos 1) not found
    self.run()
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/multiprocessing/process.py", line 114, in run
    return recv()
    return recv()
    self._target(*self._args, **self._kwargs)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/multiprocessing/pool.py", line 102, in worker
TypeError: Required argument 'handle' (pos 1) not found
TypeError: Required argument 'handle' (pos 1) not found
    task = get()
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/multiprocessing/queues.py", line 376, in get
    return recv()
TypeError: Required argument 'handle' (pos 1) not found

该错误特指我正在尝试传递的Connection实例。如果我将其设为“无”,则工作人员会毫无错误地进行分叉。

我不理解这一点,因为正如文档通过示例强调的那样,我可以轻松地将相同的参数传递给Process(),并使其完美运行:

from multiprocessing import Pipe, Process
def call_me(p):
  print("Here: %s" % (p))

(master, worker) = Pipe()
p = Process(target=call_me, args=(worker,))
p.start()

Here: <read-write Connection, handle 6>

p.join()

3 个答案:

答案 0 :(得分:1)

在此讨论中注意到此错误(http://bugs.python.org/issue4892):Python 2.6 send connection object over Queue / Pipe / etc

池最初使用管道分叉子进程,以便将任务/结果传递到子进程或从子进程传递任务/结果。它是将您的Pipe对象通过现有管道进行通信而不是分叉。 (失败是子进程在队列抽象上尝试get()的时候)。

看起来问题出现的原因是Pipe对象如何被pickle / unpickled进行通信。

在您注意到的第二种情况中,管道传递给流程实例然后分叉 - 因此行为上的差异。

我无法想象在纯任务分发之外主动与池进程通信是多处理池的预期用例。状态/协议方面,这意味着您希望更多地控制该过程。这将需要比一般Pool对象所知的更多的上下文。

答案 1 :(得分:1)

在创建池及其进程时,可以通过使用initializer和initargs参数来解决此问题。 不可否认,还必须涉及全局变量。但是,如果将工作器代码放在一个单独的模块中,它看起来并不那么糟糕。它只是全球化的过程。 : - )

典型情况是您希望工作进程将内容添加到多处理队列中。由于这与必须驻留在存储器中的某个位置的某些东西有关,因此酸洗将不起作用。即使它可行,它也只会复制有关某个进程有队列这一事实的数据。这与我们想要的相反。我们想要共享同一个队列。

所以这是一个元代码示例:

包含工作人员代码的模块,我们称之为“worker_module”:

def worker_init(_the_queue):
    global the_queue
    the_queue = _the_queue

def do_work(_a_string):
    # Add something to the queue
    the_queue.put("the string " + _a_string)

创建游泳池,然后让它做某事

# Import our functions
from worker_module import worker_init, do_work

# Good idea: Call it MPQueue to not confuse it with the other Queue
from multiprocessing import Queue as MPQueue
from multiprocessing import Pool

the_queue = MPQueue() 
# Initialize workers, it is only during initialization we can pass the_queue
the_pool = Pool(processes= 3, initializer=worker_init, initargs=[the_queue,])
# Do the work
the_pool.apply(do_work, ["my string",])
# The string is now on the queue
my_string = the_queue.get(True))

答案 2 :(得分:1)

这是一个bug,已在Python 3中修复。

最简单的解决方案是按照另一个答案中的建议将队列传递到Pool的初始化程序。