我正在使用GTK构建一个相当简单的C application,但必须执行一些阻止IO,这将触发GUI的更新。为此,我在pthread
之前开始新的gtk_main()
:
/* global variables */
GMainContext *mainc;
/* local variables */
FILE *fifo;
pthread_t reader;
/* main() */
mainc = g_main_context_default();
pthread_create(&reader, NULL, watch_fifo, argv[argc-1]);
gtk_main();
当pthread
读取某些数据时,它会更新GUI,如下所示:
g_main_context_invoke(mainc, set_icon, param);
set_icon
gboolean set_icon(gpointer data)
{
char *p = (char*)data;
gtk_status_icon_set_from_icon_name(icon, p);
return FALSE;
}
这一切都在大部分时间都有效,但每次我都会收到这个奇怪的错误信息:
[xcb] Unknown sequence number while processing queue [xcb] Most likely this is a multi-threaded client and XInitThreads has not been called [xcb] Aborting, sorry about that. mktrayicon: xcb_io.c:274: poll_for_event: Assertion `!xcb_xlib_threads_sequence_lost' failed.
我认为使用g_main_context_invoke
的重点是避免线程问题?做了一些谷歌搜索,我遇到了gdk_threads_init
,gdk_threads_enter
和朋友,但他们似乎都被弃用了?我知道GTK文档说所有的GUI更新都应该在主线程上执行,但这并不能很好地结合阻塞IO,我宁愿不必在线程之间构建一些复杂的通信机制。
所以,我的问题是,我该如何正确处理这个?
编辑:可以看到完整的代码here
EDIT2:作为基于@ ptomato答案的更新,我已经转移到GThread
并使用gdk_threads_add_idle()
提交this提交,但问题仍然存在。
答案 0 :(得分:10)
致电XInitThreads()
。这应该在gtk_init
之前完成,这将停止消息!
这样的事情:
#include <X11/Xlib.h>
...
XInitThreads();
...
gtk_init(&argc, &argv);
我不记得在GLIB 2.32之前看过这些消息
使用g_thread_init()
/ gdk_threads_init()
。
您可能需要查看g_thread_pool_new
和g_thread_pool_push
。
从线程中,使用g_main_context_invoke
在主循环或执行中执行
只需在gdk_threads_enter()
/ gdk_threads_leave()
我不使用托盘,所以我不能轻易检查这个。我想你是 使用锁来纠正gdk_threads_add_idle以保护GTK / GDK API。 对我来说没有什么可以导致这些消息 出现。 gtk_status_icon_new_from_icon_name的函数说明 说明&#34;如果当前图标主题被更改,则图标将是 适当更新。对我而言,暗示您的代码不是唯一的 将访问X显示的代码,可能是 问题
还有一些关于XInitThreads()的相关信息
What is the downside of XInitThreads()?
请注意,虽然GDK使用锁定显示,但GTK / GDK不会 调用XInitThreads。
旁注:保护全局变量的内容&#34; onclick&#34;, 在fork()之后传递给execl,子节点不会继承父节点 内存锁和GLib主循环与fork()不兼容。 也许你可以将字符串复制到局部变量。
答案 1 :(得分:1)
我不确定裸pthread是否可以保证与GTK一起使用。你应该使用GThread包装器。
我认为问题可能是g_main_context_invoke()
将set_icon()
添加为空闲功能。 (这似乎是幕后发生的事情,但我不确定。)使用GLib API添加的空闲函数尽管在主线程上执行,但需要保持GDK锁定。如果您使用gdk_threads_add_idle()
API(未弃用)来调用set_icon()
,那么一切都应该可以正常使用线程。
(虽然这只是一个疯狂的猜测。)
答案 2 :(得分:0)
作为一种解决方法,如果您只是想在等待某些IO时避免阻止UI,则可以使用GIO中的异步IO。这样可以避免你自己管理线程。
编辑:考虑一下你可以将你的文件描述符标记为非阻塞,并将它们作为源添加到glib主循环中,它将在主事件循环中为你轮询它们而不必乱用线程。
答案 3 :(得分:0)
您可以通过使用gio_add_watch()避免使用线程,当通道上有可用数据时,它会调用您的回调函数。