GTK中的线程问题

GTK+的线程安全吗?怎么去编写多线程GTK+程序呢?
       可以这样说吧,它在执行其他的GLib 调用之前调用g_thread_init()可以使GLib库工作在线程安全模式(thread-safe mode)之下.在这种模式中,GLib将会根据需要自动的锁定所有内部数据结构(internal data structures),这并不是说两个线程可以同时访问.例如:一个单一的hash表(ha
sh table),他们可以同时访问两个不同的hash表,如果两个不同的线程需要访问相同的hash表,程序将会锁定自身.
        当GLib被初始化为安全线程模式(thread-safe),GTK+能够"线程识别"(thread aware),在执
行任何的GDK调用之前,你必须通过调用gdk_threads_enter()获得一个单一全局锁定(sing
le global lock),而后又调用gdk_threads_leave()释放.

一个最小的GTK+线程程序主函数看起来象下面这样:

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

回调信号需要引起主意,GTK+中的回调信号(信号(signals))在GTK+锁定(lock)的内部,但是
GLib中的回调信号(超时,IO和系统空闲(timeouts, IO callbacks, and idle functions)
)在GTK+锁定(lock)之外,所以,在一个信号处理函数(signal handler)中你不需要调用gdk
_threads_enter(),但是在其他类型的调用中,你需要调用gdk_threads_enter().

Erik Mouw 提供了下面的代码实例说明了如何在GTK+中使用线程:

/*-------------------------------------------------------------------------
* Filename:      gtk-thread.c
* Version:       0.99.1
* Copyright:     Copyright (C) 1999, Erik Mouw
* Author:        Erik Mouw <J.A.K.Mouw@its.tudelft.nl>
* Descrīption:   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 `pkg-config gtk+-2.0 --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);
}

5.3. 当在GTK+的app文件中使用fork()时,为什么会出现奇怪的'x io error'错误?
当在GTK+的app文件中使用fork()时,为什么会出现奇怪的'x io error'错误?

这实际上并不是GTK+的问题,而且也和fork()没有关系,如果发生了'x io error',你可以使
用exit()函数来从子程序中退出.

当GDK打开了一个X显示,它创建了一个插座文件描述符(socket file descrīptor),当你使
用exit() 函数时,你默认的关闭了所有的打开文件描述符(open file descrīptors).但是
底层(underlying)的X库(X library)并不如此.

这里应该使用的函数是exit().

Erik Mouw贡献了这个代码实例来说明如何处理 fork()和exit().

/*-------------------------------------------------------------------------
* Filename:      gtk-fork.c
* Version:       0.99.1
* Copyright:     Copyright (C) 1999, Erik Mouw
* Author:        Erik Mouw <J.A.K.Mouw@its.tudelft.nl>
* Descrīption:   GTK+ fork example
* Created at:    Thu Sep 23 21:37:55 1999
* Modified by:   Erik Mouw <J.A.K.Mouw@its.tudelft.nl>
* Modified at:   Thu Sep 23 22:39:39 1999
*-----------------------------------------------------------------------*/
/*
* Compile with:
*
* cc -o gtk-fork gtk-fork.c `pkg-config gtk+-2.0 --cflags --libs`
*
*/

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <gtk/gtk.h>

void sigchld_handler(int num)
{
  sigset_t set, oldset;
  pid_t pid;
  int status, exitstatus;

  /* block other incoming SIGCHLD signals */
  sigemptyset(&set);
  sigaddset(&set, SIGCHLD);
  sigprocmask(SIG_BLOCK, &set, &oldset);

  /* wait for child */
  while((pid = waitpid((pid_t)-1, &status, WNOHANG)) > 0)
    {
      if(WIFEXITED(status))
{
  exitstatus = WEXITSTATUS(status);

  fprintf(stderr,
  "Parent: child exited, pid = %d, exit status = %d/n",
  (int)pid, exitstatus);
}
      else if(WIFSIGNALED(status))
{
  exitstatus = WTERMSIG(status);

  fprintf(stderr,
  "Parent: child terminated by signal %d, pid = %d/n",
  exitstatus, (int)pid);
}
      else if(WIFSTOPPED(status))
{
  exitstatus = WSTOPSIG(status);

  fprintf(stderr,
  "Parent: child stopped by signal %d, pid = %d/n",
  exitstatus, (int)pid);
}
      else
{
  fprintf(stderr,
  "Parent: child exited magically, pid = %d/n",
  (int)pid);
}
    }

  /* re-install the signal handler (some systems need this) */
  signal(SIGCHLD, sigchld_handler);
 
  /* and unblock it */
  sigemptyset(&set);
  sigaddset(&set, SIGCHLD);
  sigprocmask(SIG_UNBLOCK, &set, &oldset);
}

gint delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
{
  return(FALSE);
}

void destroy(GtkWidget *widget, gpointer data)
{
  gtk_main_quit();
}

void fork_me(GtkWidget *widget, gpointer data)
{
  pid_t pid;

  pid = fork();

  if(pid == -1)
    {
      /* ouch, fork() failed */
      perror("fork");
      exit(-1);
    }
  else if(pid == 0)
    {
      /* child */
      fprintf(stderr, "Child: pid = %d/n", (int)getpid());

      execlp("ls", "ls", "-CF", "/", NULL);
     
      /* if exec() returns, there is something wrong */
      perror("execlp");

      /* exit child. note the use of _exit() instead of exit() */
      _exit(-1);
    }
  else
    {
      /* parent */
      fprintf(stderr, "Parent: forked a child with pid = %d/n", (int)pid);
    }
}

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

  gtk_init(&argc, &argv);

  /* the basic stuff: make a window and set callbacks for destroy and
   * delete events
   */
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

  gtk_signal_connect(GTK_OBJECT (window), "delete_event",
     GTK_SIGNAL_FUNC(delete_event), NULL);
         
  gtk_signal_connect(GTK_OBJECT (window), "destroy",
     GTK_SIGNAL_FUNC(destroy), NULL);

#if (GTK_MAJOR_VERSION == 1) && (GTK_MINOR_VERSION == 0)
  gtk_container_border_width(GTK_CONTAINER (window), 10);
#else 
  gtk_container_set_border_width(GTK_CONTAINER (window), 10);
#endif

  /* add a button to do something usefull */
  button = gtk_button_new_with_label("Fork me!");
         
  gtk_signal_connect(GTK_OBJECT (button), "clicked",
     GTK_SIGNAL_FUNC(fork_me), NULL);

  gtk_container_add(GTK_CONTAINER(window), button);
         
  /* show everything */
  gtk_widget_show (button);
  gtk_widget_show (window);


  /* install a signal handler for SIGCHLD signals */
  signal(SIGCHLD, sigchld_handler);

 
  /* main loop */
  gtk_main ();

  exit(0);        
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值