2011-08-21 66 views
2

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

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做其他事情。

回答

1
我觉得这是beause 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(); 

// ... 

更新:从您的评论听起来好像你有破坏对话和填充组合框之间的竞争条件。一个可能的解决方案是,你碰到了组合框的引用计数(即gtk_widget_ref),这样当你异步工人做一些它没有得到释放。然后在gtk_widget_unref当其他线程不再需要指针时释放它。

+0

感谢asveikau快速回复!我在调用gtk_main()之前调用g_thread_init和gdk_threads_init: g_thread_init(NULL); gdk_threads_init(); gtk_init(&的argc,&argv); ....... gdk_threads_enter(); gtk_main()函数; gdk_threads_leave(); 有趣的是,当我破坏GTK_IS_COMBO_BOX之前的对话框,检查死锁只发生。例如,当我双击一行时,弹出对话框(大约15秒后),但是如果我双击该行并立即销毁对话框,它会挂在GTK_IS_COMBO_BOX检查上 – Tim

+0

@Tim - 读你的评论我想我看到了另一个问题,我已经更新了答案。 – asveikau

+0

感谢asveikau为您提供的所有帮助!您的解决方案奏效。 – Tim