GTK中线程的死锁问题

deadlock problem with threads in GTK

本文关键字:死锁 问题 线程 GTK      更新时间:2023-10-16

在我的GUI中,我在主窗口中有一个列表存储树视图。当用户双击一行时,会弹出一个对话框。问题是,我在对话框中填充的数据需要一段时间来处理,所以我所做的是启动一个线程(使用boost线程)来进行对话框的计算。

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_dialog_computation完成之前关闭对话框。如果对话框被销毁,则其中的组合框将被销毁,并且我对gtk_combo_box_append_text的调用将设置fault。

我尝试在更新组合框之前测试它:

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)

但这似乎也不起作用。对于如何解决这个问题有什么建议吗?

UPDATE: GTK_IS_COMBO_BOX的死锁只有在对话框打开后立即关闭时才会发生(即在do_calculation()完成之前)。如果我让对话框静止不动,它最终会更新。此外,如果我在写入前切换组合框复选框调用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 -我使用线程来做我的对话框计算,因为对话框是非模态的,我希望用户能够做其他事情与主UI,而对话框填充。

我想这是因为GTK_IS_COMBO_BOX可能调用了gdk_threads_enter()

我认为情况并非如此。这些宏通常非常简单,我不希望它带锁。事实上,据我所知,gdk_threads_enter的整个思想是库本身不应该调用它,只有知道它在另一个线程中运行的代码才应该调用它。

我的想法是:你忘记给g_thread_initgdk_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();
// ...

Update:从你的评论中,听起来你在破坏对话框和填充组合框之间有一个竞争条件。一个潜在的解决方案是增加组合框的引用计数(即gtk_widget_ref),以便在异步工作程序执行某些操作时不会释放它。然后,当其他线程不再需要该指针时,使用gtk_widget_unref释放该指针。