[Tkinter-discuss] Deadlock on graceful exit

Michael Lange klappnase at web.de
Thu May 31 18:03:31 CEST 2012


Hi,

Thus spoketh Wayne Werner <wayne at waynewerner.com> 
unto us on Thu, 31 May 2012 05:56:49 -0500 (CDT):

> Yes, though I have not looked at your code, if you're doing what you
> said and using threads in a Tkinter app. This will almost always lead
> to horrible, nasty bugs. Instead, use the .after() method in Tkinter,
> and let your app worry about timing/threading.

This is actually not true, as long as you take care that Tk always runs
in the main program thread and that you must *never* do any calls to
Tkinter from within any of the child threads; for interaction between
Tkinter and the child threads you can for example use Lock or Condition
objects to safely update some variable value from the child thread and an
after() loop to query the variable value on the Tk side.

However it was my first thought, too, that the problem might be caused by
improper separation of the gui from the child thread(s).

And now when I look at the code, I see that the child thread starts
gui_updater_body() which then calls gui.schedule() which calls
Tk.after_idle(). This, as I said above, is one of the things you must
avoid in any case, because it will lead to more or less unpredictable
behavior.

Below I wrote a primitive example of how interaction between Tk and a
child thread can be set up safely:

######################################################
from Tkinter import *
from threading import Thread, Lock
from time import sleep
from signal import signal

root = Tk()
s = IntVar()
s.set(0)
Label(root, textvariable=s).pack(pady=30, padx=70)

x = 0
run = True
lock = Lock()

def quit(*args):
    global run
    # make sure the child stops before we quit
    run = False
    root.quit()
root.protocol('WM_DELETE_WINDOW', quit)
# make sure keyboard interrupt is handled properly
signal(2, quit)

def poll():
    lock.acquire()
    s.set(x)
    print  'current value ->', x
    lock.release()
    root.after(500, poll)
poll()

def incr():
    global x
    while run:
        lock.acquire()
        x += 1
        lock.release()
        sleep(1)
    print 'child thread done'

t = Thread(target=incr)
t.start()

root.mainloop()
######################################################

Regards

Michael


.-.. .. ...- .   .-.. --- -. --.   .- -. -..   .--. .-. --- ... .--. . .-.

Violence in reality is quite different from theory.
		-- Spock, "The Cloud Minders", stardate 5818.4


More information about the Tkinter-discuss mailing list