检索pool.apply_async()结果时出现TypeError

时间:2014-08-14 17:48:43

标签: python python-2.7 multiprocessing

问题

我有一个例外,我无法确定原因,并希望有助于解决它。

背景

我在Python 2.7.6中有一个工作池,我用它来异步启动多个函数,在关闭并加入池之后,我检查ApplyResult对象以确保所有函数都成功。当我尝试检索结果时,出现以下错误:

  

追踪(最近的呼叫最后):
  文件“parse.py”,第798行,在   主()
  在主要文件中输入“parse.py”,第769行   produce_output_files(args.output_dir)
  在produce_output_files中输入文件“parse.py”,第524行   打印(result.get())
  文件“/user/Python-2.7.6/lib/python2.7/multiprocessing/pool.py”,第554行,在获取中   提高self._value
  TypeError:**之后的foo1()参数必须是映射,而不是AcquirerProxy

以下是我启动子流程的代码:

def produce_output_files(output_dir):

    pool = multiprocessing.Pool()
    manager = multiprocessing.Manager()
    db_lock = manager.Lock()
    results = [pool.apply_async(func, output_dir, db_lock) 
               for func in [foo1, foo2, foo3]]

    pool.close()
    pool.join()

    for result in results:
        if not result.successful():
            print(result.get())

    return

我的所有目标函数都具有以下结构:

def foo1(output_dir, db_lock):
    try:

        # wrapping the whole function in a try/except block because tracebacks
        # aren't pickleable, but they can be packaged into a string for pickling

    except:
        raise Exception("".join(traceback.format_exception(*sys.exc_info())))

调试步骤

是工人异常吗?

最初,我以为我只是从工人那里得到追溯,因为docs for AsyncResult说明了以下内容:

  

如果远程调用引发异常,则get()将重新启动该异常。

...而且我将回溯打包成单个字符串的方式应该会导致在主进程中打印正确的回溯。为了测试这个,我将我的被调用函数改为:

def _produce_C(output_dir, db_lock):
    raise Exception("test")

此测试产生了相同的回溯,因此我知道我没有从工作进程中打印异常(“test”从未打印过)。我相信异常是由我如何检索结果而不仅仅是从子进程传播异常引起的。

结果未就绪?

我也知道,当我在结果对象上调用get()时结果已经准备就绪,因为我已经关闭并加入了池。为了确保这一点,我将for循环更改为以下内容:

    for result in results:
        result.wait()
        if not result.successful():
            print(result.get())

这导致了相同的追溯。

工人已关闭且结果已过期?

我最后一次修复错误的方法是切换池加入的顺序并检索结果,如下所示:

    for result in results:
        result.wait()
        if not result.successful():
            print(result.get())

    pool.close()
    pool.join()

同样,会产生相同的追溯。

其他信息

this Python issue report中所述,get()方法通常不会生成完整的回溯,因为无法对回溯进行回溯。但是,在我上面显示的第一个调试测试中,如果get()实际捕获了工作者的异常,我仍应该在追溯中看到字符串“test”。此外,我在try / except块中包装函数以捕获回溯的方法在我链接到的问题报告中特别提到了解决方法。

1 个答案:

答案 0 :(得分:2)

您必须将参数传递给您使用apply_async in a tuple调用的函数:

results = [pool.apply_async(func, (output_dir, db_lock)) 
               for func in [foo1, foo2, foo3]]

这将修复异常。请考虑这是apply_async

的定义
def apply_async(self, func, args=(), kwds={}, callback=None):

以你现在的方式传递参数,你真的是这样做的:

pool.apply_async(func, args=output_dir, kwargs=db_lock)

这解释了追溯:

TypeError: foo1() argument after ** must be a mapping, not AcquirerProxy.

它正在尝试将db_lock视为kwargs。绝对不是你想要的!