forked from AuroraMiddleware/gtk
96937b32e4
* gtk/tmpl/gtkprogress.sgml: * gtk/tmpl/gtkscale.sgml: * gtk/tmpl/gtktreemodel.sgml: * gtk/tmpl/gtkimage.sgml: * gtk/tmpl/gtksignal.sgml: * gtk/tmpl/gtkrc.sgml: * gtk/gtk-docs.sgml: * gdk/gdk-docs.sgml: * gdk/tmpl/threads.sgml: * gdk/tmpl/cursors.sgml: * gdk/tmpl/rgb.sgml: Replace homegrown "nbsp", "hash" and "percent" entities by standard ISO entities "nbsp", "num" and "percnt". * gtk/text_widget.sgml: Fix a link.
284 lines
7.0 KiB
Plaintext
284 lines
7.0 KiB
Plaintext
<!-- ##### SECTION Title ##### -->
|
|
Threads
|
|
|
|
<!-- ##### SECTION Short_Description ##### -->
|
|
functions for using GDK in multi-threaded programs
|
|
|
|
<!-- ##### SECTION Long_Description ##### -->
|
|
<para>
|
|
For thread safety, GDK relies on the thread primitives in GLib,
|
|
and on the thread-safe GLib main loop.
|
|
</para>
|
|
<para>
|
|
GLib is completely thread safe (all global data is automatically
|
|
locked), but individual data structure instances are not automatically
|
|
locked for performance reasons. So e.g. you must coordinate
|
|
accesses to the same #GHashTable from multiple threads.
|
|
</para>
|
|
<para>
|
|
GTK+ is "thread aware" but not thread safe — it provides a
|
|
global lock controlled by gdk_threads_enter()/gdk_threads_leave()
|
|
which protects all use of GTK+. That is, only one thread can use GTK+
|
|
at any given time.
|
|
</para>
|
|
<para>
|
|
You must call g_thread_init() and gdk_threads_init() before executing
|
|
any other GTK+ or GDK functions in a threaded GTK+ program.
|
|
</para>
|
|
<para>
|
|
Idles, timeouts, and input functions are executed outside
|
|
of the main GTK+ lock. So, if you need to call GTK+
|
|
inside of such a callback, you must surround the callback
|
|
with a gdk_threads_enter()/gdk_threads_leave() pair.
|
|
(However, signals are still executed within the main
|
|
GTK+ lock.)
|
|
</para>
|
|
<para>
|
|
In particular, this means, if you are writing widgets that might
|
|
be used in threaded programs, you <emphasis>must</emphasis> surround
|
|
timeouts and idle functions in this matter.
|
|
</para>
|
|
<para>
|
|
As always, you must also surround any calls to GTK+ not made within
|
|
a signal handler with a gdk_threads_enter()/gdk_threads_leave() pair.
|
|
</para>
|
|
|
|
<para>A minimal main program for a threaded GTK+ application
|
|
looks like:</para>
|
|
|
|
<informalexample>
|
|
<programlisting role="C">
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
GtkWidget *window;
|
|
|
|
g_thread_init (NULL);
|
|
gdk_threads_init (<!-- -->);
|
|
gtk_init (&argc, &argv);
|
|
|
|
window = create_window (<!-- -->);
|
|
gtk_widget_show (window);
|
|
|
|
gdk_threads_enter (<!-- -->);
|
|
gtk_main (<!-- -->);
|
|
gdk_threads_leave (<!-- -->);
|
|
|
|
return 0;
|
|
}
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>
|
|
Callbacks require a bit of attention. Callbacks from GTK+ signals
|
|
are made within the GTK+ lock. However callbacks from GLib (timeouts,
|
|
IO callbacks, and idle functions) are made outside of the GTK+
|
|
lock. So, within a signal handler you do not need to call
|
|
gdk_threads_enter(), but within the other types of callbacks, you
|
|
do.
|
|
</para>
|
|
|
|
<para>Erik Mouw contributed the following code example to
|
|
illustrate how to use threads within GTK+ programs.
|
|
</para>
|
|
|
|
<informalexample>
|
|
<programlisting role="C">
|
|
/*-------------------------------------------------------------------------
|
|
* Filename: gtk-thread.c
|
|
* Version: 0.99.1
|
|
* Copyright: Copyright (C) 1999, Erik Mouw
|
|
* Author: Erik Mouw <J.A.K.Mouw@its.tudelft.nl>
|
|
* Description: GTK threads example.
|
|
* Created at: Sun Oct 17 21:27:09 1999
|
|
* Modified by: Erik Mouw <J.A.K.Mouw@its.tudelft.nl>
|
|
* Modified at: Sun Oct 24 17:21:41 1999
|
|
*-----------------------------------------------------------------------*/
|
|
/*
|
|
* Compile with:
|
|
*
|
|
* cc -o gtk-thread gtk-thread.c `gtk-config --cflags --libs gthread`
|
|
*
|
|
* Thanks to Sebastian Wilhelmi and Owen Taylor for pointing out some
|
|
* bugs.
|
|
*
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
#include <gtk/gtk.h>
|
|
#include <glib.h>
|
|
#include <pthread.h>
|
|
|
|
#define YES_IT_IS (1)
|
|
#define NO_IT_IS_NOT (0)
|
|
|
|
typedef struct
|
|
{
|
|
GtkWidget *label;
|
|
int what;
|
|
} yes_or_no_args;
|
|
|
|
G_LOCK_DEFINE_STATIC (yes_or_no);
|
|
static volatile int yes_or_no = YES_IT_IS;
|
|
|
|
void destroy (GtkWidget *widget, gpointer data)
|
|
{
|
|
gtk_main_quit (<!-- -->);
|
|
}
|
|
|
|
void *argument_thread (void *args)
|
|
{
|
|
yes_or_no_args *data = (yes_or_no_args *)args;
|
|
gboolean say_something;
|
|
|
|
for (;;)
|
|
{
|
|
/* sleep a while */
|
|
sleep(rand(<!-- -->) / (RAND_MAX / 3) + 1);
|
|
|
|
/* lock the yes_or_no_variable */
|
|
G_LOCK(yes_or_no);
|
|
|
|
/* do we have to say something? */
|
|
say_something = (yes_or_no != data->what);
|
|
|
|
if(say_something)
|
|
{
|
|
/* set the variable */
|
|
yes_or_no = data->what;
|
|
}
|
|
|
|
/* Unlock the yes_or_no variable */
|
|
G_UNLOCK (yes_or_no);
|
|
|
|
if (say_something)
|
|
{
|
|
/* get GTK thread lock */
|
|
gdk_threads_enter (<!-- -->);
|
|
|
|
/* set label text */
|
|
if(data->what == YES_IT_IS)
|
|
gtk_label_set_text (GTK_LABEL (data->label), "O yes, it is!");
|
|
else
|
|
gtk_label_set_text (GTK_LABEL (data->label), "O no, it isn't!");
|
|
|
|
/* release GTK thread lock */
|
|
gdk_threads_leave (<!-- -->);
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int main (int argc, char *argv[])
|
|
{
|
|
GtkWidget *window;
|
|
GtkWidget *label;
|
|
yes_or_no_args yes_args, no_args;
|
|
pthread_t no_tid, yes_tid;
|
|
|
|
/* init threads */
|
|
g_thread_init (NULL);
|
|
gdk_threads_init (<!-- -->);
|
|
|
|
/* init gtk */
|
|
gtk_init(&argc, &argv);
|
|
|
|
/* init random number generator */
|
|
srand ((unsigned int) time (NULL));
|
|
|
|
/* create a window */
|
|
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (window), "destroy",
|
|
GTK_SIGNAL_FUNC (destroy), NULL);
|
|
|
|
gtk_container_set_border_width (GTK_CONTAINER (window), 10);
|
|
|
|
/* create a label */
|
|
label = gtk_label_new ("And now for something completely different ...");
|
|
gtk_container_add (GTK_CONTAINER (window), label);
|
|
|
|
/* show everything */
|
|
gtk_widget_show (label);
|
|
gtk_widget_show (window);
|
|
|
|
/* create the threads */
|
|
yes_args.label = label;
|
|
yes_args.what = YES_IT_IS;
|
|
pthread_create (&yes_tid, NULL, argument_thread, &yes_args);
|
|
|
|
no_args.label = label;
|
|
no_args.what = NO_IT_IS_NOT;
|
|
pthread_create (&no_tid, NULL, argument_thread, &no_args);
|
|
|
|
/* enter the GTK main loop */
|
|
gdk_threads_enter (<!-- -->);
|
|
gtk_main (<!-- -->);
|
|
gdk_threads_leave (<!-- -->);
|
|
|
|
return 0;
|
|
}
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<!-- ##### SECTION See_Also ##### -->
|
|
<para>
|
|
|
|
</para>
|
|
|
|
<!-- ##### MACRO GDK_THREADS_ENTER ##### -->
|
|
<para>
|
|
This macro marks the beginning of a critical section in which GDK and GTK+
|
|
functions can be called. Only one thread at a time can be in such a
|
|
critial section. The macro expands to a no-op if #G_THREADS_ENABLED
|
|
has not been defined. Typically gdk_threads_enter() should be used
|
|
instead of this macro.
|
|
</para>
|
|
|
|
|
|
|
|
<!-- ##### MACRO GDK_THREADS_LEAVE ##### -->
|
|
<para>
|
|
This macro marks the end of a critical section
|
|
begun with #GDK_THREADS_ENTER.
|
|
</para>
|
|
|
|
|
|
|
|
<!-- ##### FUNCTION gdk_threads_init ##### -->
|
|
<para>
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<!-- ##### FUNCTION gdk_threads_enter ##### -->
|
|
<para>
|
|
This macro marks the beginning of a critical section
|
|
in which GDK and GTK+ functions can be called.
|
|
Only one thread at a time can be in such a critial
|
|
section.
|
|
</para>
|
|
|
|
|
|
|
|
<!-- ##### FUNCTION gdk_threads_leave ##### -->
|
|
<para>
|
|
Leaves a critical region begun with gdk_threads_enter().
|
|
</para>
|
|
|
|
|
|
|
|
<!-- ##### VARIABLE gdk_threads_mutex ##### -->
|
|
<para>
|
|
The #GMutex used to implement the critical region for
|
|
gdk_threads_enter()/gdk_threads_leave(). This variable should not be
|
|
used directly — consider it private.
|
|
</para>
|
|
|
|
|