views:

121

answers:

4

In my class I run 4 process.

from multiprocessing import Process

    procs = (
             Process(target=ClassOne, name='ClassOne'),
             Process(target=ClassTwo, name='ClassTwo'),
             Process(target=ClassThree, name='ClassThree'),
             Process(target=ClassFour, name='ClassFour'),
            )

    for p in procs:
        p.daemon = False
        p.start()

I would like to be notified when one of my children process died so i can kill the other and my self.

A: 

Here is a Windows solution. It uses WaitForMultipleObjects API call. For Unix os.waitpid may do the job.

import multiprocessing as mp
import ctypes


SYNCHRONIZE = 0x00100000
INFINITE = -1


def mbox(msg):
  ctypes.windll.user32.MessageBoxW(0, msg, u'spam', 0)


if __name__ == '__main__':

  # start 2 processes
  procs = [mp.Process(target=mbox, args=(msg,)) for msg in u'ab']
  for p in procs:
    p.start()

  # wait for at least one process to terminate
  handles = [
    ctypes.windll.kernel32.OpenProcess(SYNCHRONIZE, False, p.pid)
    for p in procs]
  array_type = ctypes.c_long * len(handles)
  handle_array = array_type(*handles)
  ctypes.windll.kernel32.WaitForMultipleObjects(
    len(handles), handle_array, False, INFINITE)

  # terminate the rest
  for p in procs:
    p.terminate()

  # exit
  print 'done'
Constantin
The issue is that waitpid() takes only **one** pid and is blocking until timeout ... WaitForMultipleObjects() does the job as it can handle many objects at the same time... Can you provide a POC using waitpid() ?
Spì
@Spi, as you apparently found out yourself, `waitpid` can be used to wait for "i-dont-care-which-one" child to die. The problem is that you may have other children spawned and `waitpid` will signal their termination too...
Constantin
+1  A: 

The simplest way would be to explicitly wait until all processes are done.

while multiprocessing.active_children():
    pass

This is not event driven as you might intend, but it will get the job done in your example. Also make sure you import multiprocessing.

cdmayer
OP wants to terminate when *at least one* child dies, not when all of them die.
Constantin
+1  A: 

Just define a signal handler for SIGCHLD, inspect the frame returned by the just dead child to retrieve the information you need about it ... and if necessary exit() the parent too :)

AlberT
@Spi, if you were looking for a Unix-specific solution you should have indicated this in the question.
Constantin
@Constantin, Win does not have signals? I'm really curious, no polemic :)
AlberT
Windows has a C signal API, but doesn't use them natively. In Windows, you can trivially wait on child process handles. You would simply wait for the first to become signalled, then terminate the others and then exit yourself.
MSalters
Well, i guess your answer implies something like `signal.signal(SIGCHLD, handler)`. But http://docs.python.org/library/signal.html says "On Windows, signal() can only be called with SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, or SIGTERM. A ValueError will be raised in any other case." No SIGCHLD.
Constantin
@Constantin, you are right and I was not saing the opposite... I was asking you if and how signals are handled on Windows as I don't know ... @MSalters explained it very cleanly, thanks to him!
AlberT
+2  A: 

It is possible to use os.waitpid() passing -1 as the first argument and 0 as the second one.

  • The first argument means that the request pertains to any child of the current process.
  • The second argument means that it behaves as wait().

The function returns a tuple with the pid of the dead child and its exit code.

Spì