Search code examples
pythonmultithreadinggobject

GObject.idle_add(), thread.join() and my program hangs


I've a problem: my main thread should start and join a thread that has a to run about 10-15 seconds.

So:

  • my main thread starts a child thread, whom has to run about 10-15 seconds
  • my main thread joins the child thread: the main thread needs the result of the child thread
  • in the meanwhile the child thread runs, i've to update the GUI, it is just a progressbar (possibly every 0.2 second)
  • updating the GUI: i'll use a recursive function
  • the child thread finishes and the main thread gets the result

The problem with my code is that the Trovarete_Window appears and starts to be updated only when the worker function has already ended. So my program hangs/freezes until the worker runs.

Here is my code:

def collega_GUI(widget, args=()):

    builder.get_object('Trovarete_Window').show_all()
    progressbar.set_pulse_step(0.20)

    pulse()
    t = threading.Thread(target=worker)
    t.setDaemon(True)
    t.start()
    t.join()
    print 'a'


def pulse():

    if not event.is_set():
        progressbar.pulse()
        GObject.timeout_add(300, pulse)


def worker():
    #hard work

Solution

  • The problem is that you're calling t.join() inside collegea_GUI. Calling t.join() means your main thread - the thread that's running the event loop - is blocked until worker completes. As long as you're blocked inside collegea_GUI, your main loop cannot run, which means the call to pulse you scheduled can't run, and the GUI can't be updated.

    Instead of calling t.join(), you'll need to do something similar to what you're doing with pulse - schedule a function to run that does a non-blocking check to see if worker is finished. Something like this:

    def collega_GUI(widget, args=()):
    
        builder.get_object('Trovarete_Window').show_all()
        progressbar.set_pulse_step(0.20)
    
        pulse()
        t = threading.Thread(target=worker)
        t.setDaemon(True)
        t.start()
        wait_for_t(t)
    
    def wait_for_t(t):
       if not t.is_alive():
           t.join() # This won't block, since the thread isn't alive anymore
           print 'a'
           # Do whatever else you would do when join() (or maybe collega_GUI?) returns
       else:
           Gobject.timeout_add(200, wait_for_t, t)