Re: [Tkinter-discuss] Proposal for thread-safe Tkinter
All, After a very helpful discussion with "Guilherme Polo <ggpolo@gmail.com>", we have come to the following conclusion regarding multithreaded applications and Tkinter. Although Tk is technically thread-safe (if built with --enable-threads), practically speaking it still has problems when used in multithreaded applications. The problem stems from the fact that _tkinter attempts to gain control of the main thread via a polling technique. If it succeeds, all is well. If it fails, however, the application receives an exception with the message "RuntimeError: main thread is not in main loop". There is no way to tell when this might happen, so calling Tk routines from multiple threads seems to be problematic at best. The mtTkinter module I published last week (http://tkinter.unpythonic.net/wiki/mtTkinter) solves this problem by marshaling calls coming from multiple threads through a queue which is read by an 'after' event running periodically in the main loop. This is similar to the technique used in many other platforms (e.g., .NET's InvokeRequired/Invoke mechanism). The technique used in mtTkinter is exception-safe as well, marshaling exceptions through a response queue back to the caller's thread. If a similar technique were employed in _tkinter instead of the polling technique, that would be a preferable solution. In the meantime, mtTkinter will work as a good stop-gap measure. Allen B. Taylor
On Mon, Oct 27, 2008 at 12:59 PM, Allen Taylor <Allen.Taylor@mdacorporation.com> wrote:
All, After a very helpful discussion with "Guilherme Polo <ggpolo@gmail.com>", we have come to the following conclusion regarding multithreaded applications and Tkinter. Although Tk is technically thread-safe (if built with --enable-threads), practically speaking it still has problems when used in multithreaded applications.
Now that I reread it, I see you said "Tk is technically thread-safe (if built with --enable-threads)". It is not true, what happens is that tkinter makes it thread safe by using some mutexes and scheduling actions to go through the thread that created the tcl interpreter, which otherwise would happen elsewhere. But tk isn't thread safe per se (only tcl, since version 8.1). At the same time, if tk is compiled without thread support but python has thread support, then tkinter fails miserably to protect against multiple threads from python from accessing tk. And again, your module does help in this case since all the interaction to tk will occur through main thread, independent of tk having thread support or not.
The problem stems from the fact that _tkinter attempts to gain control of the main thread via a polling technique. If it succeeds, all is well. If it fails, however, the application receives an exception with the message "RuntimeError: main thread is not in main loop". There is no way to tell when this might happen, so calling Tk routines from multiple threads seems to be problematic at best. The mtTkinter module I published last week (http://tkinter.unpythonic.net/wiki/mtTkinter) solves this problem by marshaling calls coming from multiple threads through a queue which is read by an 'after' event running periodically in the main loop. This is similar to the technique used in many other platforms (e.g., .NET's InvokeRequired/Invoke mechanism). The technique used in mtTkinter is exception-safe as well, marshaling exceptions through a response queue back to the caller's thread. If a similar technique were employed in _tkinter instead of the polling technique, that would be a preferable solution. In the meantime, mtTkinter will work as a good stop-gap measure. Allen B. Taylor
-- -- Guilherme H. Polo Goncalves
wait a second... I have a class system, and to exercise this class system, and possibly as a general command line tool in the future, I access this class from a script meant to run in a shell. To watch progress of the system as it runs (it's processing data, and can take significant amounts of time) so I thought I'd make a nice little interface to watch the progress, and let the user pause and do other control commands. This is not the purpose of this command line tool, however, so I only bring that up based on a command line flag. Also, I do not call mainloop from the main thread, because it is doing the processing, and is the "real" program. I start another thread, use it (and only it) as the Tk thread. This all works fine, and I never get the exception mentioned below. -craig On Mon, 2008-10-27 at 15:10 -0200, Guilherme Polo wrote:
On Mon, Oct 27, 2008 at 12:59 PM, Allen Taylor <Allen.Taylor@mdacorporation.com> wrote:
All, After a very helpful discussion with "Guilherme Polo <ggpolo@gmail.com>", we have come to the following conclusion regarding multithreaded applications and Tkinter. Although Tk is technically thread-safe (if built with --enable-threads), practically speaking it still has problems when used in multithreaded applications.
Now that I reread it, I see you said "Tk is technically thread-safe (if built with --enable-threads)". It is not true, what happens is that tkinter makes it thread safe by using some mutexes and scheduling actions to go through the thread that created the tcl interpreter, which otherwise would happen elsewhere. But tk isn't thread safe per se (only tcl, since version 8.1). At the same time, if tk is compiled without thread support but python has thread support, then tkinter fails miserably to protect against multiple threads from python from accessing tk.
And again, your module does help in this case since all the interaction to tk will occur through main thread, independent of tk having thread support or not.
The problem stems from the fact that _tkinter attempts to gain control of the main thread via a polling technique. If it succeeds, all is well. If it fails, however, the application receives an exception with the message "RuntimeError: main thread is not in main loop". There is no way to tell when this might happen, so calling Tk routines from multiple threads seems to be problematic at best. The mtTkinter module I published last week (http://tkinter.unpythonic.net/wiki/mtTkinter) solves this problem by marshaling calls coming from multiple threads through a queue which is read by an 'after' event running periodically in the main loop. This is similar to the technique used in many other platforms (e.g., .NET's InvokeRequired/Invoke mechanism). The technique used in mtTkinter is exception-safe as well, marshaling exceptions through a response queue back to the caller's thread. If a similar technique were employed in _tkinter instead of the polling technique, that would be a preferable solution. In the meantime, mtTkinter will work as a good stop-gap measure. Allen B. Taylor
On Tue, Oct 28, 2008 at 6:33 PM, C. Allen <callen@gemini.edu> wrote:
wait a second... I have a class system, and to exercise this class system, and possibly as a general command line tool in the future, I access this class from a script meant to run in a shell. To watch progress of the system as it runs (it's processing data, and can take significant amounts of time) so I thought I'd make a nice little interface to watch the progress, and let the user pause and do other control commands. This is not the purpose of this command line tool, however, so I only bring that up based on a command line flag. Also, I do not call mainloop from the main thread, because it is doing the processing, and is the "real" program. I start another thread, use it (and only it) as the Tk thread.
As long as other calls to tk come from the same thread that created the interpreter, there is no need to schedule these calls.
This all works fine, and I never get the exception mentioned below.
The message in that error is more in the sense of "the thread that created the tcl interpreter is not in main loop". It is also very hard to get it with just two threads. It will try ten times, which will last 1 second (as the current implementation), at max, to acquire the thread that created the tcl interpreter, only then if this fails you get that error.
-craig
On Mon, 2008-10-27 at 15:10 -0200, Guilherme Polo wrote:
On Mon, Oct 27, 2008 at 12:59 PM, Allen Taylor <Allen.Taylor@mdacorporation.com> wrote:
All, After a very helpful discussion with "Guilherme Polo <ggpolo@gmail.com>", we have come to the following conclusion regarding multithreaded applications and Tkinter. Although Tk is technically thread-safe (if built with --enable-threads), practically speaking it still has problems when used in multithreaded applications.
Now that I reread it, I see you said "Tk is technically thread-safe (if built with --enable-threads)". It is not true, what happens is that tkinter makes it thread safe by using some mutexes and scheduling actions to go through the thread that created the tcl interpreter, which otherwise would happen elsewhere. But tk isn't thread safe per se (only tcl, since version 8.1). At the same time, if tk is compiled without thread support but python has thread support, then tkinter fails miserably to protect against multiple threads from python from accessing tk.
And again, your module does help in this case since all the interaction to tk will occur through main thread, independent of tk having thread support or not.
The problem stems from the fact that _tkinter attempts to gain control of the main thread via a polling technique. If it succeeds, all is well. If it fails, however, the application receives an exception with the message "RuntimeError: main thread is not in main loop". There is no way to tell when this might happen, so calling Tk routines from multiple threads seems to be problematic at best. The mtTkinter module I published last week (http://tkinter.unpythonic.net/wiki/mtTkinter) solves this problem by marshaling calls coming from multiple threads through a queue which is read by an 'after' event running periodically in the main loop. This is similar to the technique used in many other platforms (e.g., .NET's InvokeRequired/Invoke mechanism). The technique used in mtTkinter is exception-safe as well, marshaling exceptions through a response queue back to the caller's thread. If a similar technique were employed in _tkinter instead of the polling technique, that would be a preferable solution. In the meantime, mtTkinter will work as a good stop-gap measure. Allen B. Taylor
_______________________________________________ Tkinter-discuss mailing list Tkinter-discuss@python.org http://mail.python.org/mailman/listinfo/tkinter-discuss
-- -- Guilherme H. Polo Goncalves
ah, thanks again Guilherme. That was the principle I was following when I tried to make this well behaved (I'd had a worse, and badly behaved, first version). It was important for Tk not to have the main thread, since the GUI is an addendum for what is generally a command line tool. cheers, craig On Tue, 2008-10-28 at 18:52 -0200, Guilherme Polo wrote:
On Tue, Oct 28, 2008 at 6:33 PM, C. Allen <callen@gemini.edu> wrote:
wait a second... I have a class system, and to exercise this class system, and possibly as a general command line tool in the future, I access this class from a script meant to run in a shell. To watch progress of the system as it runs (it's processing data, and can take significant amounts of time) so I thought I'd make a nice little interface to watch the progress, and let the user pause and do other control commands. This is not the purpose of this command line tool, however, so I only bring that up based on a command line flag. Also, I do not call mainloop from the main thread, because it is doing the processing, and is the "real" program. I start another thread, use it (and only it) as the Tk thread.
As long as other calls to tk come from the same thread that created the interpreter, there is no need to schedule these calls.
This all works fine, and I never get the exception mentioned below.
The message in that error is more in the sense of "the thread that created the tcl interpreter is not in main loop". It is also very hard to get it with just two threads. It will try ten times, which will last 1 second (as the current implementation), at max, to acquire the thread that created the tcl interpreter, only then if this fails you get that error.
-craig
On Mon, 2008-10-27 at 15:10 -0200, Guilherme Polo wrote:
On Mon, Oct 27, 2008 at 12:59 PM, Allen Taylor <Allen.Taylor@mdacorporation.com> wrote:
All, After a very helpful discussion with "Guilherme Polo <ggpolo@gmail.com>", we have come to the following conclusion regarding multithreaded applications and Tkinter. Although Tk is technically thread-safe (if built with --enable-threads), practically speaking it still has problems when used in multithreaded applications.
Now that I reread it, I see you said "Tk is technically thread-safe (if built with --enable-threads)". It is not true, what happens is that tkinter makes it thread safe by using some mutexes and scheduling actions to go through the thread that created the tcl interpreter, which otherwise would happen elsewhere. But tk isn't thread safe per se (only tcl, since version 8.1). At the same time, if tk is compiled without thread support but python has thread support, then tkinter fails miserably to protect against multiple threads from python from accessing tk.
And again, your module does help in this case since all the interaction to tk will occur through main thread, independent of tk having thread support or not.
The problem stems from the fact that _tkinter attempts to gain control of the main thread via a polling technique. If it succeeds, all is well. If it fails, however, the application receives an exception with the message "RuntimeError: main thread is not in main loop". There is no way to tell when this might happen, so calling Tk routines from multiple threads seems to be problematic at best. The mtTkinter module I published last week (http://tkinter.unpythonic.net/wiki/mtTkinter) solves this problem by marshaling calls coming from multiple threads through a queue which is read by an 'after' event running periodically in the main loop. This is similar to the technique used in many other platforms (e.g., .NET's InvokeRequired/Invoke mechanism). The technique used in mtTkinter is exception-safe as well, marshaling exceptions through a response queue back to the caller's thread. If a similar technique were employed in _tkinter instead of the polling technique, that would be a preferable solution. In the meantime, mtTkinter will work as a good stop-gap measure. Allen B. Taylor
_______________________________________________ Tkinter-discuss mailing list Tkinter-discuss@python.org http://mail.python.org/mailman/listinfo/tkinter-discuss
participants (3)
-
Allen Taylor -
C. Allen -
Guilherme Polo