Re: GTK and threads
- From: Michael Torrie <torriem chem byu edu>
- To: kornelix <kornelix yahoo de>
- Cc: gtk app dev mail list <gtk-app-devel-list gnome org>
- Subject: Re: GTK and threads
- Date: Mon, 06 Feb 2006 12:22:08 -0700
kornelix wrote:
Thanks. I eagerly anticipate your example.
I have attached a sample file that uses threads in the way you described
in your last post. The gui consists of a single text display and a
button. When you click the button, a new thread is started that
periodically (every second) sends a message to the text window. When
you close the window, the program will send a signal to each thread to
terminal (well not a real signal, just sets a flag). Once all the
threads have terminated, the program ends. I have not implemented a
proper signalling system to send signals to the threads nor have I used
the pthread_join to properly rejoin threads that have quit. Mainly
because pthread_join blocks and that causes problems.
You'll notice the on_destroy callback has a loop that iterates the gtk
event loop while we're waiting for the threads to stop. This is because
we're in a callback, already holding the gdk_thread_lock, so any waiting
in here will prevent threads from aquiring their lock and thus finishing
their loops. So we iterate it.
The callbacks (including the pthread_create calls) already occur in the
context of the main gdk lock, so no explicit locking is needed there.
See what you think.
I made a small code example like the one you described. It hung up. Then
I figured out why: I am calling a function that has the
gdk_threads_enter() and _leave() wrappers from main() as well as from a
created thread. In the case of main(), the call to gdk_threads_enter()
was a second call for that thread, and this is apparently fatal. I have
to figure out how to detect and avoid this, or make multiple versions of
the functions with and without the wrappers. If I call the function from
the thread only, it works.
I really am not following what you are describing (please provide code)
but it feels like you're definitely doing something wrong. It needn't
be this complicated from my experience.
One way of looking at the issue is that the FAQ example includes all
main() code within the thread wrapper functions, meaning that any called
functions better not have the wrappers. Would it be legitimate to treat
main() like any other thread, and put the wrappers only around gtk/gdk
calls? If I could do this, then functions would be usable from both
main() and created threads.
Hmm. Sounds complicated. My gut feeling is that most of your
difficulties are in not knowing when you are in the gdk lock already and
when you need to initiate a lock. Please tell me if my example code
addresses any of the problems you have been having. (and for the real
GTK experts out there, feel free to point out where I'm doing everything
wrong.)
Michael
It will not take long, so I will just try this.
regards
#include <stdio.h>
#include <stdlib.h>
#include <gtk/gtk.h>
#include <glib.h>
#include <pthread.h>
gint thread_count=0;
gint dead_threads;
GSList *threads;
struct thread_info_data {
GtkTextBuffer *buffer;
gint id;
pthread_t tid;
gint terminate;
};
void thread (void *data) {
struct thread_info_data *info;
GtkTextIter iter;
GString *message;
info=(struct thread_info_data *)data;
message=g_string_new("");
while(info->terminate == 0 ) {
sleep(1);
g_string_printf(message,"Thread %d here.\n",info->id);
g_print("%s",message->str);
gdk_threads_enter();
gtk_text_buffer_get_end_iter(info->buffer,&iter);
gtk_text_buffer_insert(info->buffer,&iter,message->str,-1);
gdk_threads_leave();
}
g_print("Thread %d stopping.\n",info->id);
gdk_threads_enter(); //borrowing a lock from gtk to lock our variable
dead_threads++;
gdk_threads_leave(); //we could and should use a counting semaphore
g_string_free(message,NULL);
}
void stop_thread(gpointer data, gpointer user_data) {
struct thread_info_data *thread;
thread=data;
thread->terminate=1;
g_print("Asked thread %d to stop.\n",thread->id);
//here we ought to make sure the thread really has died
// pthread_join(thread->tid,NULL);
}
void on_destroy(GtkWidget *widget, gpointer data) {
g_slist_foreach(threads,stop_thread,NULL);
while(dead_threads < thread_count) {
while(gtk_events_pending()) {
gtk_main_iteration();
}
sleep(0);
}
gtk_main_quit();
}
void on_button_clicked(GtkWidget *widget, gpointer data) {
GtkTextBuffer *buffer;
GtkTextIter iter;
struct thread_info_data *thread_info;
buffer=GTK_TEXT_BUFFER(data);
thread_info=g_malloc(sizeof(struct thread_info_data));
thread_info->buffer=buffer;
thread_info->id=thread_count++;
thread_info->terminate=0;
threads=g_slist_append(threads,thread_info);
pthread_create(&(thread_info->tid),NULL,thread,thread_info);
gtk_text_buffer_get_end_iter(buffer,&iter);
gtk_text_buffer_insert(buffer,&iter,"Button clicked!\n",-1);
g_print("Button clicked.\n");
}
int main(int argc, char *argv[]) {
GtkWidget *window;
GtkWidget *button;
GtkWidget *vbox;
GtkWidget *viewarea;
GtkWidget *view;
GtkWidget *buffer;
threads=NULL;
g_thread_init (NULL);
gdk_threads_init ();
gtk_init(&argc, &argv);
window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_signal_connect(GTK_OBJECT(window),"destroy",
GTK_SIGNAL_FUNC(on_destroy),NULL);
gtk_container_set_border_width(GTK_CONTAINER(window),10);
vbox=gtk_vbox_new(FALSE,5);
gtk_container_add(GTK_CONTAINER(window),vbox);
gtk_widget_show(vbox);
viewarea=gtk_scrolled_window_new(NULL,NULL);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(viewarea),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
gtk_box_pack_start(GTK_BOX(vbox),viewarea,TRUE,TRUE,2);
gtk_widget_show(viewarea);
view=gtk_text_view_new();
buffer=gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
gtk_text_buffer_set_text(buffer,"The threads will write here.\n",-1);
gtk_container_add(GTK_CONTAINER(viewarea),view);
gtk_widget_show(view);
button=gtk_button_new_with_label("Click me");
gtk_signal_connect(GTK_OBJECT(button),"clicked",
GTK_SIGNAL_FUNC(on_button_clicked),buffer);
gtk_box_pack_end(GTK_BOX(vbox),button,FALSE,FALSE,3);
gtk_widget_show(button);
gtk_widget_show(window);
gdk_threads_enter ();
gtk_main();
gdk_threads_leave ();
return 0;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]