GTK中线程的死锁问题
Posted
技术标签:
【中文标题】GTK中线程的死锁问题【英文标题】:deadlock problem with threads in GTK 【发布时间】:2011-08-21 18:50:29 【问题描述】:在我的 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 执行其他操作。
【问题讨论】:
【参考方案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
释放它。
【讨论】:
感谢 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 - 阅读您的评论我想我看到了另一个问题。我已经更新了答案。以上是关于GTK中线程的死锁问题的主要内容,如果未能解决你的问题,请参考以下文章