最近被GTK的界面死掉问题困扰。GTK本来就不是线程安全的,但是有些应用,确实需要有不止一个线程访问图形界面的问题。我查了很多资料,基本上都是说开gthread支持,然后在需要访问界面的代码前后加上gdk_threads_enter()和gdk_threads_leave()。
但是很不幸,在我的实践中,这样的界面会在不确定的死掉。虽然,从逻辑上来说,这样做是合理的。我并不知道是为什么,
现在也没有时间去看gtk+的实现代码。只能临时找到一个解决方案,就是使用gtk_add_idle函数,添加一个刷新任务。
大致就是
gboolean refresh_ui(gpointer data)
{
//refresh operations
return FALSE;
}
…….
gtk_add_idle(refresh_ui, data);
目前使用中没有问题,现在先记下来,回头在有时间再研究为什么。
的确,我也碰到跟楼主一样的问题,不得已也是通过g_idle_add来解决的,根据经验,对于比较简单的例子用gdk_threads_enter倒也没问题,我的程序中潜入了一个浏览器,用gdk_threads_enter貌似不行
恩,写得很详尽,谢谢!
其实,gtk_idle_add()的原理就是手动地使程序回到main loop中,是这样吧?
我猜gtk_idle_add()比gdk_threads_enter()更有效的原因是,gtk_idle_add()是程序员手动指定,交给main loop控制执行的,每次遇到gtk_idle_add()的时候,总能够把所有的刷新任务执行完,而且,不用考虑mutex是否锁定。 而gdk_threads_enter()则由内核控制,且在刷新数据时受到mutex的影响,如果你设定的mutex lock不当,那么新的线程在设定mutex lock时就会阻塞。 你把GUI和数据绑到一起,使用gtk_idle_add()确实要安全得多,不过理论上讲也比threads更耗资源。
没有见过——也不可能见到你的代码,权且这样瞎猜一下。
to Kermit:
抱歉,是我手误了,估计你搜到的也是别人写错了。因为gthread支持属于glib库对thread的封装,故初始化线程环境等对线程环境设置的函数是g_thread_,比如,g_thread_init(),但是这两个进入界面访问的临界去的函数是属于gdk库的,因为到了图形一层就是属于gdk支持的内容了,这两个函数名我已经改成正确的了gdk_threads_enter()和gdk_threads_leave()。gdk中对线程的支持是用gdk_threads_打头的。
另外,事实上,gtk+中主消息循环gtk_main()是一定要维护界面的,而且贯穿整个程序的生命周期,事实上用gtk_idle_add()和gtk_timeout_add()这样的函数,就是在主循环中去访问界面,也间接的让只有主循环所在的线程去访问界面,所以要比使用gdk_threads_enter()和gdk_threads_leave()直接去访问界面稳定性要好一些,至少我目前的经验是这样。
恩,我理解你说的,MVC在某些时候所带来的通信成本确实很大,尤其是当用户需要直接在视图上跟这些数据交互时(如Excel表格那种)。
还有个问题,老大,g_thread_enter()和g_thread_leave()我搜到有人在用,但为什么我在官方的文档和主页上找不到呢?
to luguo:
哈哈,就是这么假~
to Kermit:
事实上,你说的是可以的,MVC设计模式就是一种,将数据的存储和表示分开。但是有时候设计是比较复杂的。在嵌入式系统上,而且往往受到硬件资源和成本的限制,在数据量巨大的时候,通信成本也是比较高的,往往造成整体成本上升。不过你的思路是对的,只是实现的时候往往需要对成本和其他一些事情作出妥协。程序设计,就是妥协的艺术~所以,就这样了~
“有些应用,确实需要有不止一个线程访问图形界面的问题。”
请教个问题:能否通过一些设计上的改良,让GUI与线程控制无关? 我觉得线程是程序的功能部分,而GUI不过是个视图而已,按理说应该可以分开吧。
(注:我是提问的,不是提建议的;p)
老大居然也写技术文章。。。。。太不可思议了~~~~