Re: GUI freeze by changing label from thread



Am Mittwoch, den 17.01.2007, 23:28 +0100 schrieb Andreas Volz:
> Hello,
> 
> I have a thread that does try to change a label in a window. Sometimes
> (nearly often) the window does "freeze". Freeze means that the window
> isn't longer redrawn. So the window is empty, but it does still react
> on events (e.g. mouse press). Here is a sketch of what I do:
[snip]
> I've no idea at the moment why this does freeze? Could you imagine why
> the GUI isn't redrawn?

Yup.  Because you're accessing the GUI from multiple threads.  GTK+ and
indeed Xlib itself are not thread-safe.  Well, actually that's only half
of the story, as GDK provides the functions gdk_threads_enter() and
gdk_threads_leave() to acquire and release a global lock around all the
GUI stuff.

http://developer.gnome.org/doc/API/2.0/gdk/gdk-Threads.html

However, there are problems with this:

a) It's ugly and leads to messy application design.
b) GUI access is serialized, so the parallelism benefit of threads goes
down the drain.
c) It works with the X backend only.
d) gtkmm doesn't really support it.  It might work for some uses if
you're lucky.  However, gtkmm was not designed to support this usage, so
it'll most likely break in subtle ways.

This probably sounds all very off-putting.  But read on.  It's not that
gtkmm developers simply don't care about threads.  There is a way.  I'd
call it the Proper Way (tm).

The solution is to use a client-server model.  All GUI work is done by
the main thread, and only by the main thread.  The other threads do all
the number crunching, I/O, or whatever you want them to do.  These other
threads then act as clients of the GUI server.  If a thread needs to
have the UI act in some way -- for instance, updating a progress bar --
it asynchronously sends a message to the GUI thread, which is handled
the next time the GUI gives control back to the main loop.

glibmm comes with special support for this programming model.  Have a
look at Glib::Dispatcher:

http://www.gtkmm.org/docs/glibmm-2.4/docs/reference/html/classGlib_1_1Dispatcher.html

Glib::Dispatcher is similar to sigc::signal<void> but implements
asynchronous inter-thread communication.  There's also a usage example
linked to in the documentation.

Alternatively, for simple one-shot invocation of some function in the
main thread, you may also use Glib::signal_idle().  Unfortunately using
signal_idle() from multiple threads is currently not entirely safe due
to a recently discovered bug:

http://bugzilla.gnome.org/show_bug.cgi?id=396958

This bug will be fixed though, so by all means use it if it makes sense
in your code.

Getting used to all this stuff will probably require a fair bit of work
and thought on your side.  But it really pays off.  Multi-threading is a
delicate matter and all to easy to get wrong.  The client-server model
enforces a design in which the processing of data is cleanly separated
from the display of that data.  That will greatly reduce the hours spent
in the debugger later on.

Happy threading,
--Daniel





[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]