2

I have the following code (just testing some assumptions about the Pool class):

#!/usr/bin/env python
from time import sleep
from multiprocessing import Pool

def run_process(args):
    print "Sleeping %d seconds" % (args * 2)
    sleep(args * 2)
    print "Slept %d seconds" % (args * 2)

def main():
    test = [1,2,3,1,2,3,1,2,3]
    pool = Pool()
    pool.map_async(run_process, test).get()

if __name__ == '__main__':
    main()

Eventually test will become a list of arguments to be passed to each invocation of run_process in order for it to spawn a command (i.e. a separate process).

Now when I press Ctrl+C it unfortunately doesn't even tear down the immediate child processes (from the documentation it sounds like the Pool creates simply child processes). What happens on the terminal (Ubuntu 10.04, Python 2.6.5, if this matters) is that I see the echoed ^C whenever I press it, but neither does the parent die in time when all the children should have exited from the "job" (i.e. sleeping) nor was I able to reclaim the terminal without killing the parent process.

How can I adjust my script so that it will tear down the immediate children (and later on also the spawned commands)? I suppose what I'm trying is to use multiprocessing.Pool.close() and multiprocessing.Pool.terminate() from a signal handler?!

N.B.: I'm intentionally using processes here instead of threads, so I'd appreciate if the solution would take that into account.


Here's one thing I tried after reading through the answers to this question, but it suffers from the same problem:

#!/usr/bin/env python
from signal import signal
from time import sleep
from multiprocessing import Pool

def init_worker():
    import signal
    signal(signal.SIGINT, signal.SIG_IGN)

def run_process(args):
    print "Sleeping %d seconds" % (args * 2)
    sleep(args * 2)
    print "Slept %d seconds" % (args * 2)

def main():
    test = [1,2,3,1,2,3,1,2,3]
    try:
        pool = Pool()
        pool.map_async(run_process, test).get()
    except KeyboardInterrupt:
        pool.terminate()
        pool.wait()

if __name__ == '__main__':
    main()

Adding an insanely large timeout (the jobs can run several weeks, so the timeout must be big enough to cover that) to the Pool.get() appears to allow me to use Ctrl+C, although the output is somewhat confusing:

Traceback (most recent call last):
  File "./test.py", line 26, in <module>
    main()
  File "./test.py", line 23, in main
    pool.wait()
AttributeError: 'Pool' object has no attribute 'wait'

after getting rid of try/except, the traceback looks "more natural". So the trick is to change the line from the original script pool.map_async(run_process, test).get() to something like pool.map_async(run_process, test).get(999999) where 999999 is a timeout big enough to suit your purpose.

Community
  • 1
  • 1
0xC0000022L
  • 20,597
  • 9
  • 86
  • 152
  • Does this help? http://jessenoller.com/2009/01/08/multiprocessingpool-and-keyboardinterrupt/ – mgilson Jul 02 '12 at 19:25
  • Or this: http://stackoverflow.com/a/1408476/748858 – mgilson Jul 02 '12 at 19:27
  • @mgilson: not sure in how far this differs from using `map_async` and catching the `KeyboardInterrupt` (see my edit, and yes I edited it while you wrote that comment :)), but it seems not. – 0xC0000022L Jul 02 '12 at 19:35
  • @mgilson: actually adding the timeout fixed the issue. Thanks. It's somewhat inconvenient because I have to account for very long running jobs, but it appears to work. – 0xC0000022L Jul 02 '12 at 19:42
  • possible duplicate of [Keyboard Interrupts with python's multiprocessing Pool](http://stackoverflow.com/questions/1408356/keyboard-interrupts-with-pythons-multiprocessing-pool) – 0xC0000022L Jul 02 '12 at 19:42

0 Answers0