Exception thrown in multiprocessing Pool not detected

后端 未结 9 749
灰色年华
灰色年华 2020-11-29 18:44

It seems that when an exception is raised from a multiprocessing.Pool process, there is no stack trace or any other indication that it has failed. Example:

         


        
相关标签:
9条回答
  • 2020-11-29 19:31

    The solution with the most votes at the time of writing has a problem:

    from multiprocessing import Pool
    
    def go():
        print(1)
        raise Exception("foobar")
        print(2)
    
    p = Pool()
    x = p.apply_async(go)
    x.get()  ## waiting here for go() to complete...
    p.close()
    p.join()
    

    As @dfrankow noted, it will wait on x.get(), which ruins the point of running a task asynchronously. So, for better efficiency (in particular if your worker function go takes a long time) I would change it to:

    from multiprocessing import Pool
    
    def go(x):
        print(1)
        # task_that_takes_a_long_time()
        raise Exception("Can't go anywhere.")
        print(2)
        return x**2
    
    p = Pool()
    results = []
    for x in range(1000):
        results.append( p.apply_async(go, [x]) )
    
    p.close()
    
    for r in results:
         r.get()
    

    Advantages: the worker function is run asynchronously, so if for example you are running many tasks on several cores, it will be a lot more efficient than the original solution.

    Disadvantages: if there is an exception in the worker function, it will only be raised after the pool has completed all the tasks. This may or may not be the desirable behaviour. EDITED according to @colinfang's comment, which fixed this.

    0 讨论(0)
  • 2020-11-29 19:36

    Maybe I'm missing something, but isn't that what the get method of the Result object returns? See Process Pools.

    class multiprocessing.pool.AsyncResult

    The class of the result returned by Pool.apply_async() and Pool.map_async().get([timeout])
    Return the result when it arrives. If timeout is not None and the result does not arrive within timeout seconds then multiprocessing.TimeoutError is raised. If the remote call raised an exception then that exception will be reraised by get().

    So, slightly modifying your example, one can do

    from multiprocessing import Pool
    
    def go():
        print(1)
        raise Exception("foobar")
        print(2)
    
    p = Pool()
    x = p.apply_async(go)
    x.get()
    p.close()
    p.join()
    

    Which gives as result

    1
    Traceback (most recent call last):
      File "rob.py", line 10, in <module>
        x.get()
      File "/usr/lib/python2.6/multiprocessing/pool.py", line 422, in get
        raise self._value
    Exception: foobar
    

    This is not completely satisfactory, since it does not print the traceback, but is better than nothing.

    UPDATE: This bug has been fixed in Python 3.4, courtesy of Richard Oudkerk. See the issue get method of multiprocessing.pool.Async should return full traceback.

    0 讨论(0)
  • 2020-11-29 19:45
    import logging
    from multiprocessing import Pool
    
    def proc_wrapper(func, *args, **kwargs):
        """Print exception because multiprocessing lib doesn't return them right."""
        try:
            return func(*args, **kwargs)
        except Exception as e:
            logging.exception(e)
            raise
    
    def go(x):
        print x
        raise Exception("foobar")
    
    p = Pool()
    p.apply_async(proc_wrapper, (go, 5))
    p.join()
    p.close()
    
    0 讨论(0)
提交回复
热议问题