How can I handle KeyboardInterrupt events with python's multiprocessing Pools? Here is a simple example:
from multiprocessing import Pool
from time import sleep
from sys import exit
def slowly_square(i):
sleep(1)
return i*i
def go():
pool = Pool(8)
try:
results = pool.map(slowly_square, range(40))
except KeyboardInterrupt:
# **** THIS PART NEVER EXECUTES. ****
pool.terminate()
print "You cancelled the program!"
sys.exit(1)
print "\nFinally, here are the results: ", results
if __name__ == "__main__":
go()
When running the code above, the KeyboardInterrupt
gets raised when I press ^C
, but the process simply hangs at that point and I have to kill it externally.
I want to be able to press ^C
at any time and cause all of the processes to exit gracefully.
For some reasons, only exceptions inherited from the base
Exception
class are handled normally. As a workaround, you may re-raise yourKeyboardInterrupt
as anException
instance:Normally you would get the following output:
So if you hit
^C
, you will get:Usually this simple structure works for Ctrl-C on Pool :
As was stated in few similar posts:
Capture keyboardinterrupt in Python without try-except
It seems there are two issues that make exceptions while multiprocessing annoying. The first (noted by Glenn) is that you need to use
map_async
with a timeout instead ofmap
in order to get an immediate response (i.e., don't finish processing the entire list). The second (noted by Andrey) is that multiprocessing doesn't catch exceptions that don't inherit fromException
(e.g.,SystemExit
). So here's my solution that deals with both of these:Strangely enough it looks like you have to handle the
KeyboardInterrupt
in the children as well. I would have expected this to work as written... try changingslowly_square
to:That should work as you expected.
I found, for the time being, the best solution is to not use the multiprocessing.pool feature but rather roll your own pool functionality. I provided an example demonstrating the error with apply_async as well as an example showing how to avoid using the pool functionality altogether.
http://www.bryceboe.com/2010/08/26/python-multiprocessing-and-keyboardinterrupt/
You can try using the apply_async method of a Pool object, like this:
Output:
An advantage of this method is that results processed before interruption will be returned in the results dictionary: