在我的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_calculations中的do_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 - 我使用线程进行对话框计算,因为对话框是非模态的,我希望用户能够在填充对话框的同时使用主UI执行其他操作。
答案 0 :(得分:1)
我认为这是因为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
释放它。