2011-08-21 141 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