GTK+ FAQ
<<< Previous | Development with GTK+: general questions | Next >>>

# Is GTK+ thread safe? How do I write multi-threaded GTK+ applications?

The GLib library can be used in a thread-safe mode by calling g_thread_init() before making any other GLib calls. In this mode GLib automatically locks all internal data structures as needed. This does not mean that two threads can simultaneously access, for example, a single hash table, but they can access two different hash tables simultaneously. If two different threads need to access the same hash table, the application is responsible for locking itself.

When GLib is intialized to be thread-safe, GTK+ is thread aware. There is a single global lock that you must acquire with gdk_threads_enter() before making any GDK calls, and release with gdk_threads_leave() afterwards.

A minimal main program for a threaded GTK+ application looks like:

    int
    main (int argc, char *argv[])
    {
      GtkWidget *window;

      g_thread_init(NULL);
      gtk_init(&argc, &argv);

      window = create_window();
      gtk_widget_show(window);

      gdk_threads_enter();
      gtk_main();
      gdk_threads_leave();

      return(0);
    }

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.

Erik Mouw contributed the following code example to illustrate how to use threads within GTK+ programs.

    /*-------------------------------------------------------------------------
     * 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);

      /* 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);
    }

<<< Previous | Home | Next >>>
Development with GTK+: general questions | Up | Why does this strange 'x io error' occur when I `fork()` in my GTK+ app?