Search code examples
pythonpython-2.7multiprocessinggtk3pygobject

Python, Multiprocessing and GUI


I have a program with a GUI that needs to do some multiprocessing. The point of it is to avoid freezing the GUI, and to allow the user using some other buttons while the process is running.

I'd like then to define a method like the following:

def start_waiting(self, parent, func, args):

    self.window_waiting.show()

    task=Process(func(*args))
    task.start()

    task.join()

    # Some code to execute at the end of the process
    #
    #...

The problem is that join() is not working, and I need it because the code executed after join() indicates when the Process has ended. I'll use that code to update a cancel button of the window_waiting.

I then came in mind with another solution to avoid usingjoin(), I replaced it with:

while task.is_alive():
    time.sleep(0.5)

But it didn't worked neither, so I tried a plan C, which was to create a queue:

def worker(input, output):
    for func, args in iter(input.get, 'STOP'):
        result=func(*args)
        output.put(result)


task_queue = Queue()
done_queue = Queue()
task_queue.put(task)

Process(target=worker, args=(task_queue, done_queue)).start()
done_queue.get()

The last code gave me the error: 'Pickling an AuthenticationString object is ' TypeError: Pickling an AuthenticationString object is disallowed for security reasons

Which lead me to multiprocessing.Process subclass using shared queue but I haven't managed to solve the problem :/


Solution

  • Your first example should look like this:

    def start_waiting(self,parent,func,args):
    
        ...not relevant code..
    
        self.window_waiting.show()
    
    
        task=Process(target=func, args=args)  # This line is different.
        task.start()
    
    
        task.join()
    

    The way you had it, it wasn't actually executing func in a child process; it was executing it in the parent, and then passing the return value to Process. When you called task.start(), it would probably instantly fail, because you were passing it whatever func returned, rather than a function object.

    Note that because you're calling task.join() inside start_waiting, it's probably going to block your GUI, because start_waiting won't return until func completes, even though its running in a child process. The only way it wouldn't block is if you're running start_waiting in a separate thread from the GUI's event loop. You probably want something more like this:

    def start_waiting(self,parent,func,args):
    
        ...not relevant code..
    
        self.window_waiting.show() # You should interact with the GUI in the main thread only.
    
        self.task = Process(target=func, args=args)  # This line is different.
        self.thrd = threading.Thread(target=self.start_and_wait_for_task)
        self.thrd.start()
    
    
    def start_and_wait_for_task(self):
        """ This runs in its own thread, so it won't block the GUI. """
        self.task.start()
        self.task.join()
        # If you need to update the GUI at all here, use GLib.idle_add (see https://wiki.gnome.org/Projects/PyGObject/Threading)
        # This is required to safely update the GUI from outside the main thread.