18

我正在使用 GTK 构建一个相当简单的C 应用程序,但必须执行一些阻塞 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_initgdk_threads_enter朋友,但他们似乎都被弃用了?我知道 GTK 文档说所有 GUI 更新都应该在主线程上执行,但这并不能很好地与阻塞 IO 结合起来,而且我不希望在线程之间构建一些复杂的通信机制。

所以,我的问题是,我应该如何正确处理这个问题?

编辑:可以在这里看到完整的代码 EDIT2:作为基于@ptomato 答案的更新,我已经移至GThreads 并使用了,如本次gdk_threads_add_idle()提交中所见,但问题仍然存在。

4

4 回答 4

18

打电话XInitThreads()。这应该在之前完成gtk_init,这将停止消息!

像这样的东西:

    #include <X11/Xlib.h>
    ...  
    XInitThreads();
    ...
    gtk_init(&argc, &argv);

我不记得在 使用g_thread_init()/时在 GLIB 2.32 之前看到过这些消息gdk_threads_init()

您可能想签出g_thread_pool_newg_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() 不兼容。也许您可以将字符串复制到局部变量。

于 2013-09-09T01:55:15.580 回答
1

我不确定是否保证裸 pthreads 可以与 GTK 一起使用。您应该使用 GThread 包装器。

我认为问题可能g_main_context_invoke()是添加set_icon()为空闲功能。(这似乎是幕后发生的事情,但我不确定。)使用 GLib 的 API 添加的空闲函数,尽管是在主线程上执行的,但需要持有 GDK 锁。如果您使用gdk_threads_add_idle()API(不推荐使用)来调用set_icon(),那么一切都应该与线程一起正常工作。

(虽然这只是一个疯狂的猜测。)

于 2013-09-06T06:08:51.560 回答
0

您可以通过使用 gio_add_watch() 来避免使用线程,当通道上有可用数据时,它将调用您的回调函数。

于 2013-09-10T06:56:00.580 回答
0

作为一种解决方法,如果您只是想避免在等待一些 IO 时阻塞 UI,您可以使用来自GIO的异步 IO 。这样可以避免您必须自己管理线程。

编辑:考虑一下,您可以将文件描述符标记为非阻塞并将它们作为源添加到 glib 主循环中,它会在主事件循环中为您轮询它们,而不必弄乱线程。

于 2013-09-09T09:13:23.820 回答