тупиковая проблема с потоками в GTK - PullRequest
2 голосов
/ 21 августа 2011

В моем графическом интерфейсе у меня есть главное окно списка магазинов.Когда пользователь дважды щелкает строку, появляется диалоговое окно.Проблема в том, что для заполнения данных, которые я заполняю в диалоговом окне, требуется некоторое время, поэтому я запустил поток (используя потоки наддува) для выполнения расчетов в диалоговом окне.

In main:
.......
g_signal_connect (G_OBJECT (m_treeview), "row_activated", G_CALLBACK (m_row_activated),
                  (gpointer) main_window);
.......

In m_row_activated:
.........
// combo_box and dialog are GtkWidget* global variables 
create_dialog(dialog, combo_box); // function creates the combobox
set_combo_box_with_loading_message;
gtk_widget_show_all (dialog);
thread m_thread (bind (&do_dialog_calculations, data1, data2, combobox));
.........

In do_dialog_calculations:
.........
// do_calculations takes about 15 seconds to complete
do_calculations(MyData data1, MyData data2, combobox);
gdk_threads_enter();
gtk_combo_box_append_text(...);
gdk_threads_leave()

Всеработает нормально (т. е. когда пользователь дважды щелкает строку, сразу же появляется всплывающее окно с сообщением о загрузке, и оно заполняется в конце концов, когда возвращается поток), но моя проблема заключается в том, что пользователь закрывает диалоговое окно до завершения do_calculations в do_dialog_calculations.Если диалоговое окно уничтожено, мой выпадающий список внутри него будет уничтожен, и мой вызов gtk_combo_box_append_text вызовет ошибку.

Я пытался проверить поле со списком перед его обновлением:

In do_dialog_calculations:
.........
do_calculations(MyData data1, MyData data2, combobox);
gdk_threads_enter();
if (GTK_IS_COMBO_BOX (combobox))
   gtk_combo_box_append_text(...);
gdk_threads_leave()

, но это приводит к тупику при вызове GTK_IS_COMBO_BOX.Я думаю, что это потому, что GTK_IS_COMBO_BOX, вероятно, вызывает gdk_threads_enter ().Я также пробовал тестировать NULL

 if (combobox == NULL)

, но это тоже не сработало.Любые предложения о том, как обойти эту проблему?

ОБНОВЛЕНИЕ: тупик в GTK_IS_COMBO_BOX возникает, только если я закрываю диалоговое окно сразу после его открытия (то есть до завершения do_calculations (). Если я просто оставлю диалог, онбудет в конечном итоге обновляться. Также, если я переключу проверку выпадающего списка перед тем, как написать вызов gdk_threads_enter ():

if (GTK_IS_COMBO_BOX (combobox)
{
   gdk_threads_enter();
   gtk_combo_box_append_text(...);
   gdk_threads_leave();
}

Когда я уничтожаю диалоговое окно до выполнения этого кода, не возникает тупиковая ситуация. Однако я боюсь редкоговероятность того, что пользователь закроет диалоговое окно после завершения проверки GTK_IS_COMBO_BOX.

PS - я использую потоки, чтобы выполнить вычисления в моем диалоговом окне, потому что диалоговые окна не являются модальными, и я хочу, чтобы пользователь мог делать другиевещи с основным пользовательским интерфейсом, в то время как диалоговые окна заполняются.

1 Ответ

1 голос
/ 21 августа 2011
Я думаю, это потому, что GTK_IS_COMBO_BOX, вероятно, вызывает gdk_threads_enter ()

Не думаю, что это так.Эти макросы, как правило, довольно просты, и я не ожидал, что они получат блокировку.На самом деле, насколько я могу судить, вся идея gdk_threads_enter заключается в том, что сама библиотека не должна вызывать это, должен только код, который знает, что она работает в другом потоке.

Вот моя идея:не забудьте вызвать g_thread_init и gdk_threads_init?

Кроме того, нужно помнить одну вещь ... По умолчанию gdk_threads_enter не использует рекурсивный мьютекс.Хотя некоторые люди имеют религиозные возражения против рекурсивных мьютексов, можно gdk_threads_enter использовать один:

static GStaticRecMutex my_gdk_lock;

static void my_gdk_lock_enter() {g_static_rec_mutex_lock(&my_gdk_lock);}
static void my_gdk_lock_leave() {g_static_rec_mutex_unlock(&my_gdk_lock);}

// ...

   g_thread_init(NULL);

   g_static_rec_mutex_init(&my_gdk_lock);

   gdk_threads_set_lock_functions(G_CALLBACK(my_gdk_lock_enter),
                                  G_CALLBACK(my_gdk_lock_leave));

   gdk_threads_init();

// ...

Обновление: Из вашего комментария кажется, что у вас есть расы между уничтожениемдиалоговое окно и заполнение поля со списком.Одно из возможных решений - увеличить счетчик ссылок поля со списком (т. Е. gtk_widget_ref), чтобы он не освобождался, пока ваш асинхронный работник что-то делает.Затем отпустите его с помощью gtk_widget_unref, когда другой поток больше не нуждается в указателе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...