Keyboard Interrupts with python's multiprocessing Pool Keyboard Interrupts with python's multiprocessing Pool python python

Keyboard Interrupts with python's multiprocessing Pool


This is a Python bug. When waiting for a condition in threading.Condition.wait(), KeyboardInterrupt is never sent. Repro:

import threadingcond = threading.Condition(threading.Lock())cond.acquire()cond.wait(None)print "done"

The KeyboardInterrupt exception won't be delivered until wait() returns, and it never returns, so the interrupt never happens. KeyboardInterrupt should almost certainly interrupt a condition wait.

Note that this doesn't happen if a timeout is specified; cond.wait(1) will receive the interrupt immediately. So, a workaround is to specify a timeout. To do that, replace

    results = pool.map(slowly_square, range(40))

with

    results = pool.map_async(slowly_square, range(40)).get(9999999)

or similar.


From what I have recently found, the best solution is to set up the worker processes to ignore SIGINT altogether, and confine all the cleanup code to the parent process. This fixes the problem for both idle and busy worker processes, and requires no error handling code in your child processes.

import signal...def init_worker():    signal.signal(signal.SIGINT, signal.SIG_IGN)...def main()    pool = multiprocessing.Pool(size, init_worker)    ...    except KeyboardInterrupt:        pool.terminate()        pool.join()

Explanation and full example code can be found at http://noswap.com/blog/python-multiprocessing-keyboardinterrupt/ and http://github.com/jreese/multiprocessing-keyboardinterrupt respectively.


For some reasons, only exceptions inherited from the base Exception class are handled normally. As a workaround, you may re-raise your KeyboardInterrupt as an Exception instance:

from multiprocessing import Poolimport timeclass KeyboardInterruptError(Exception): passdef f(x):    try:        time.sleep(x)        return x    except KeyboardInterrupt:        raise KeyboardInterruptError()def main():    p = Pool(processes=4)    try:        print 'starting the pool map'        print p.map(f, range(10))        p.close()        print 'pool map complete'    except KeyboardInterrupt:        print 'got ^C while pool mapping, terminating the pool'        p.terminate()        print 'pool is terminated'    except Exception, e:        print 'got exception: %r, terminating the pool' % (e,)        p.terminate()        print 'pool is terminated'    finally:        print 'joining pool processes'        p.join()        print 'join complete'    print 'the end'if __name__ == '__main__':    main()

Normally you would get the following output:

staring the pool map[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]pool map completejoining pool processesjoin completethe end

So if you hit ^C, you will get:

staring the pool mapgot ^C while pool mapping, terminating the poolpool is terminatedjoining pool processesjoin completethe end