[docs] [issue33479] Document tkinter and threads

Ivan Pozdeev report at bugs.python.org
Mon May 14 22:16:41 EDT 2018


Ivan Pozdeev <ivan_pozdeev at mail.ru> added the comment:

> This seems very complicated. The official line on threads for Tk has always been to make all Tk calls from one thread, which is at least predictable and comprehensible. Is there any reason for Tkinter to suggest anything different?

Tcl/Tk doesn't have a notion of a blocking event loop like other GUI toolkits do. Any code using Tcl must essentially call Tcl_DoOneEvent/`update` regularly at strategic points.
This allows to work completely in one thread -- i.e. even if the OS has no threads whatsoever. Tcl/Tk is very old, and this model made perfect sense back then (and still does in some scenarios -- e.g. https://stackoverflow.com/questions/4083796/how-do-i-run-unittest-on-a-tkinter-app -- so there's no point in dropping it).
If we'll be updating tutorials (the reference is a priority though), we definitly need to demonstrate this option.

The current best practice for GUI programming is different. There's one "GUI" thread that runs just the event loop constantly, and other threads submit GUI-related work items into its queue (whatever they are called - "messages", "events", "futures"...). Likewise, for any lengthy task, the GUI thread spawns worker threads that report back on their progress via the same queue.

All more or less modern GUI toolkits implement and advertize this model as the primary one -- as does Tkinter. So, at least the work item submitting API must be thread-safe (and in other GUI tooltikts, it is -- see https://mail.python.org/pipermail/python-dev/2018-May/153359.html ).
For programmer's convenience, Tkinter does this transparently: whenever and whatever Python thread a call is made from, it makes it look for the Tcl interpreter as if all calls are sequential, and it enforces correct order for interdependent calls.

A great deal of complexity comes from the fact that Tcl's threading model is very unothodox. Tcl's team seem to only have accepted threads reluctantly and to have been campaigning against threads for years before that (https://wiki.tcl.tk/38128 is arguably the most egregious case).
So, what they did is tie a Tcl interpreter to an OS thread (by using thread local storage for its data). Long story short, no-one else does it like this (at least, I've never seen or heard of anything of the kind), and this is not what thread-local storage is meant for (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4324.html lists some use cases for TLS). The best practice is to use locks to ensure orderly access to the shared state instead. My guess for the decision is it was the easiest way to migrate the code base (yet still tough as https://wiki.tcl.tk/1370 seems to hint), and it kinda lines up with that "single thread" mindset.

Tkinter thus has to jump through hoops for calls coming from other threads (since for Python, it absolutely doesn't matter which OS thread makes a call).
All the limitations when using threaded Tcl (see the letter attached to the ticket for details) come solely from this tying. With nonthreaded Tcl (bugs nonwithstanding), it's free-for-all, everything can be called from everywhere. The only upside is that with threaded Tcl, calls to different interpreters can run in parallel.

> This ignores the compilation issue of course. FYI, the Tcl core group will probably eliminate the possibility of doing non-threaded builds in the future, though with backwards compatibility issues, that's neither here nor there.

I know. Me asking them for clarifications from Tcl/Tk's side seems to have triggered it, in fact. Since the sole offender is their threading model, the way is to show them how it's defective and work towards improving it. We have at least a few years with old versions, and the limitations seem tolerable at first glance anyway, so that's not a priority.

> do you know how to identify the tcl/tk build on MacOS or Linux?

The same way Tkinter does @_tkinter.c:624 . The equivalent Python is `tkinter.Tk().tk.call("array","get","tcl_platform","threaded")`

----------

_______________________________________
Python tracker <report at bugs.python.org>
<https://bugs.python.org/issue33479>
_______________________________________


More information about the docs mailing list