Отправляет email-рассылки с помощью сервиса Sendsay

Практические советы по GTK+

  Все выпуски  

Практические советы по GTK+ (Работа с потоками. часть 2)


 GTK+ имеет такую неприятную особенность, заключающуюся в том, что  GDK и GTK+ функции не должны выполняться одновременно в нескольких потоках, в противном случае некорректное поведение программы обеспечено, вплоть до краха приложения.

Функции-обработчики сигналов уже защищены от одновременного выполнения, но если Вы создаёте дополнительный поток и собираетесь в нем работать с интерфейсом  GDK или GTK+, тогда графические функции в потоке Вы должны защитить самостоятельно .

Для защиты GDK и GTK+ функций во многопотоковом приложении их нужно экранировать с помощью gdk_threads_enter() и gdk_threads_leave().

Рассмотрим подробнее функции, которые нам понадобятся:

void gdk_threads_init(void)

иннициализация возможности использования функций gdk_threads_enter() и gdk_threads_leave()

void gdk_threads_enter(void)

начало критической секции в которой безопасно могут быть вызваны GDK и GTK+ функции.

void gdk_threads_leave(void)

конец критической секции, начатой в gdk_threads_enter().

 

Пример: [Жирным шрифтом выделены функции, ради которых показан этот пример]

// потоковая функция

gpointer thread_func(gpointer data)

{

       GtkWidget *label = GTK_LABEL(data);

       // вход в критическую секцию для защиты gtk_label_set_text()

       gdk_threads_enter ();

       gtk_label_set_text(label,"new text");

       // выход из критической секции

       gdk_threads_leave ();

       return 0;

}

int main (int argc, char *argv[])

{

 GtkWidget *window;

 GtkWidget *label;

// иннициализация потоков [вызывается до gdk_threads_init()]

 g_thread_init (NULL);

// иннициализация потоков для защиты GDK и GTK+ функций [вызывается до gtk_main()]

 gdk_threads_init ();

// вход в критическую секцию

 gdk_threads_enter ();

 gtk_init (&argc, &argv);

// Главное окно создаем;

 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

// обработчики сигналов завершения

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

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

// метка - единственный виджет в окне

 label = gtk_label_new("old text");

 gtk_container_add (GTK_CONTAINER (window), label);

 gtk_widget_show_all(window);

 

// создать поток, в дополнительном параметре передаём указатель на текстовую метку

 g_thread_create(thread_func,label,FALSE,NULL);

 gtk_main();

// выход из критической секции

 gdk_threads_leave ();

return 0;

}

 

Из примера видно, что во всех потоках, включая основной, GDK и GTK+ функции находятся в пределах критической секции, которая защищает код от одновременного выполнения.

Если внимательно посмотрь код, то будет видно, что функция gdk_threads_leave() в основном потоке стоит после gtk_main(), и по идее в дополнительном потоке функция gdk_threads_enter() не должна пускать внутрь критической секции до конца завершения gtk_main(), т.е. до завершения приложения. Но оказывается  внутри gtk_main() выполняются gdk_threads_leave() и gdk_threads_enter() при обработке очереди сообщений, поэтому внутри дополнительного потока критическая секция будет доступна во время выполнения gtk_main().

P.S. (Все выпуски, включая этот, можно найти в одном файле gtk_book.zip)

 


В избранное