我正在构建一个相当简单的 C申请 使用GTK,但必须执行一些阻止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]处理队列时的序列号未知
[xcb]很可能这是一个多线程客户端,而且还没有调用XInitThreads
[xcb]中止,对不起。
mktrayicon:xcb_io.c:274:poll_for_event:断言`!xcb_xlib_threads_sequence_lost'失败。
我想到了使用的全部意义 g_main_context_invoke
是为了避免线程问题?做了一些谷歌搜索,我遇到了 gdk_threads_init
, gdk_threads_enter
和朋友们,但他们似乎都被弃用了?我知道GTK文档说所有的GUI更新都应该在主线程上执行,但这并不能很好地结合阻塞IO,我宁愿不必在线程之间构建一些复杂的通信机制。
所以,我的问题是,我该如何正确处理这个?
编辑:可以看到完整的代码 这里
EDIT2:作为基于@ ptomato答案的更新,我已经搬到了 GThread
和使用 gdk_threads_add_idle()
正如所见 这个 提交,但问题仍然存在。
呼叫 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的函数说明
声明“如果当前图标主题已更改,则图标将为
适当更新。对我而言,暗示您的代码不是唯一的
将访问X显示的代码,可能是
问题。
还有一些关于XInitThreads()的相关信息
XInitThreads()的缺点是什么?
请注意,虽然GDK使用锁定显示,但GTK / GDK不会
调用XInitThreads。
在旁注:什么保护全局变量“onclick”,哪个
在fork()之后传递给execl,子节点不会继承父节点
内存锁和GLib主循环与fork()不兼容。
也许你可以将字符串复制到局部变量。
我不确定裸pthreads是否可以保证与GTK一起使用。你应该使用GThread包装器。
我想问题可能是什么 g_main_context_invoke()
正在加入 set_icon()
作为空闲功能。 (看来这就是幕后的情况,但我不确定。)使用GLib API添加的空闲函数,尽管在主线程上执行,但需要保持GDK锁定。如果你使用 gdk_threads_add_idle()
要调用的API(不推荐使用) set_icon()
,然后一切都应该与线程正常工作。
(虽然这只是一个疯狂的猜测。)
作为一种解决方法,如果您只是想在等待某些IO时避免阻止UI,则可以使用异步IO GIO。这样可以避免您自己管理线程。
编辑:考虑一下你可以将你的文件描述符标记为非阻塞,并将它们作为源添加到glib主循环中,它将在主事件循环中为你轮询它们而不必乱用线程。
您可以通过使用gio_add_watch()避免使用线程,当通道上有可用数据时,它会调用您的回调函数。