0

For a program that should run both on Linux and Windows (python 2.7), I'm trying to update values of a given object using multiprocessing.Process (while the main program is running, I'm calling the update class by a separate process).

Sometimes it takes too long before my object is updated, so I want to be able to kill my update process, and to continue with the main program. "Too long" is not strictly defined here, but rather a subjective perception of the user. For a single queue (as in the MyFancyClass example in http://pymotw.com/2/multiprocessing/communication.html) I can kill the update process and the main program continues as I want. However, when I make a second queue to retrieve the updated object, ending the update process does not allow me to continue in the main program.

What I have so far is:

import multiprocessing
import time, os

class NewParallelProcess(multiprocessing.Process):

    def __init__(self, taskQueue, resultQueue, processName):
        multiprocessing.Process.__init__(self)
        self.taskQueue      = taskQueue
        self.resultQueue    = resultQueue
        self.processName    = processName


    def run(self):
        print "pid %s of process that could be killed" % os.getpid()
        while True:
            next_task = self.taskQueue.get()
            if next_task is None:
                # poison pill for terminate
                print "%s: exiting" % self.processName
                self.taskQueue.task_done()
                break
            print "%s: %s" % (self.processName, next_task)
            answer = next_task()
            self.taskQueue.task_done()
            self.resultQueue.put(answer)

        return



class OldObject(object):
    def __init__(self):
        self.accurate       = "OldValue"
        self.otherValue     = "SomeOtherValue"



class UpdateObject(dict):
    def __init__(self, objectToUpdate):
        self.objectToUpdate = objectToUpdate

    def __call__(self):
        returnDict = {}
        returnDict["update"]    = self.updateValue("NewValue")
        return returnDict

    def __str__(self):
        return "update starting"

    def updateValue(self, updatedValue):
        for i in range(5):
            time.sleep(1) # updating my object - time consuming with possible pid kill
            print "working... (pid=%s)" % os.getpid()

        self.objectToUpdate.accurate = updatedValue
        return self.objectToUpdate




if __name__ == '__main__':
    taskQueue   = multiprocessing.JoinableQueue()
    resultQueue     = multiprocessing.Queue()
    newProcess  = NewParallelProcess(taskQueue, resultQueue, processName="updateMyObject")
    newProcess.start()

    myObject = OldObject()  
    taskQueue.put(UpdateObject(myObject))

    # poison pill for NewParallelProcess loop and wait to finish
    taskQueue.put(None)
    taskQueue.join()

    # get back results
    results = resultQueue.get()
    print "Values have been updated"
    print "---> %s became %s" % (myObject.accurate, results["update"].accurate)

Any suggestions on how to kill the newProcess and to continue in the main program?

Well, made some modifications, and this does what I want. Not sure whether it is the most efficient, so any improvements are always welcome :)

import multiprocessing
import time, os

class NewParallelProcess(multiprocessing.Process):

    def __init__(self, taskQueue, resultQueue, processName):
        multiprocessing.Process.__init__(self)
        self.taskQueue      = taskQueue
        self.resultQueue    = resultQueue
        self.name       = processName


    def run(self):
        print "Process %s (pid = %s) added to the list of running processes" % (self.name, self.pid)
        next_task = self.taskQueue.get()

        self.taskQueue.task_done()
        self.resultQueue.put(next_task())

        return



class OldObject(object):
    def __init__(self):
        self.accurate       = "OldValue"
        self.otherValue     = "SomeOtherValue"



class UpdateObject(dict):
    def __init__(self, objectToUpdate, valueToUpdate):
        self.objectToUpdate = objectToUpdate
        self.valueToUpdate = valueToUpdate


    def __call__(self):
        returnDict = {}
        returnDict["update"]    = self.updateValue(self.valueToUpdate)
        return returnDict


    def updateValue(self, updatedValue):
        for i in range(5):
            time.sleep(1) # updating my object - time consuming with possible pid kill
            print "working... (pid=%s)" % os.getpid()

        self.objectToUpdate.accurate = updatedValue
        return self.objectToUpdate




if __name__ == '__main__':
    # queue for single process
    taskQueue   = multiprocessing.JoinableQueue()
    resultQueue     = multiprocessing.Queue()
    newProcess  = NewParallelProcess(taskQueue, resultQueue, processName="updateMyObject")
    newProcess.start()

    myObject = OldObject()  
    taskQueue.put(UpdateObject(myObject, "NewValue"))


    while True:
        # check if newProcess is still alive
        time.sleep(5)
        if newProcess.is_alive() is False:
            print "Process %s (pid = %s) is not running any more (exit code = %s)" % (newProcess.name, newProcess.pid, newProcess.exitcode)
            break


    if newProcess.exitcode == 0:
        print "ALL OK"
        taskQueue.join()

        # get back results
        print "NOT KILLED"

        results = resultQueue.get()
        print "Values have been updated"
        print "---> %s became %s" % (myObject.accurate, results["update"].accurate)


    elif newProcess.exitcode == 1:
        print "ended with error in function"
        print "KILLED"
        for i in range(5):
            time.sleep(1)
            print "i continue"


    elif newProcess.exitcode == -15 or newProcess.exitcode == -9:
        print "ended with kill signal %s" % newProcess.exitcode
        print "KILLED"
        for i in range(5):
            time.sleep(1)
            print "i continue"


    else:
        print "no idea what happened"
        print "KILLED"
        for i in range(5):
            time.sleep(1)
            print "i continue"
LievenB
  • 13
  • 4
  • I think the signalling on the `taskQueue.task_done()` and `taskQueue.join()` is unnecessary in this scenario. You could simply delete those lines. A half-baked solution to your problem could be using `resultQueue.get()` with a timeout. This still has a timeout and doesn't respond to your kill immediately. On linux systems such problems are usually handled with a `SIGCHLD` handler that immediately tells the parent when a child dies. The `SIGCHLD` handler could then somehow unblock the waiting method call (with a dummy message or dummy `task_done()` if you are using this kind of messaging). – pasztorpisti Aug 17 '15 at 00:04
  • It seems to work for me on Linux -- what does it do for you and what do you think it should do? – Patrick Maupin Aug 18 '15 at 01:27
  • @PatrickMaupin: the problem was that when killing the parallel process, my main got stuck. I wanted a main that could handle the killing of the parallel process, and then continue to exit "gracefully". I added the check for is_alive() in the main and recuperated the stop signal from the parallel process. – LievenB Aug 18 '15 at 08:22

0 Answers0