C语言 GTK 的线程问题
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18647475/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Threading problems with GTK
提问by Jon Gjengset
I'm building a fairly simple C applicationusing GTK, but have to perform some blocking IO which will trigger updates to the GUI. In order to do this, I start a new pthreadright before gtk_main()as such:
我正在使用 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();
When the pthreadreads some data, it updates the GUI like so:
当pthread读取一些数据时,它会像这样更新 GUI:
g_main_context_invoke(mainc, set_icon, param);
Where set_iconis
哪里set_icon是
gboolean set_icon(gpointer data)
{
char *p = (char*)data;
gtk_status_icon_set_from_icon_name(icon, p);
return FALSE;
}
This all works most of the time, but every now and again I get this curious error message:
大多数情况下,这一切都有效,但我时不时地收到以下奇怪的错误消息:
[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.
I thought the whole point of using g_main_context_invokewas to avoid issues with threads? Doing a bit of Googling, I came across gdk_threads_init, gdk_threads_enterand friends, but they all seem to be deprecated? I know the GTK documentation says that all GUI updaes should be performed on the main thread, but this does not combine all that well with blocking IO, and I'd prefer not to have to construct some complex communication mechanism between the threads.
我认为使用的全部目的g_main_context_invoke是避免线程问题?做了一些谷歌搜索,我碰到了gdk_threads_init,gdk_threads_enter和朋友,但他们似乎都被弃用?我知道 GTK 文档说所有 GUI 更新都应该在主线程上执行,但这并不能很好地与阻塞 IO 结合,而且我不想在线程之间构建一些复杂的通信机制。
And so, my question is, how should I correctly deal with this?
所以,我的问题是,我应该如何正确处理这个问题?
EDIT: The full code can be seen hereEDIT2: As an update based on @ptomato's answer, I've moved to GThreads and using gdk_threads_add_idle()as seen in thiscommit, but the problem is still present.
编辑:可以在此处查看完整代码
EDIT2:作为基于@ptomato 答案的更新,我已移至GThreads 并gdk_threads_add_idle()在此提交中使用,但问题仍然存在。
采纳答案by Wiley
Call XInitThreads(). This should be done before gtk_init, that will stop the messages!
打电话XInitThreads()。这应该在之前完成gtk_init,这将停止消息!
Something like this:
像这样的东西:
#include <X11/Xlib.h>
...
XInitThreads();
...
gtk_init(&argc, &argv);
I don't remember seeing these messages before GLIB 2.32, when
g_thread_init()/gdk_threads_init()were used.
我不记得在使用g_thread_init()/时在 GLIB 2.32 之前看到过这些消息
gdk_threads_init()。
You might want to check out g_thread_pool_newand g_thread_pool_push.
From thread, use g_main_context_invoketo execute in main loop or
just wrap thread between gdk_threads_enter()/gdk_threads_leave()
您可能想查看g_thread_pool_new和g_thread_pool_push。从线程,用于g_main_context_invoke在主循环中执行或只是在gdk_threads_enter()/之间包装线程gdk_threads_leave()
I do not use a tray so I can not easily check this. I think you are correct about gdk_threads_add_idle using locks to protect GTK/GDK API. There is nothing obvious to me that would cause these messages to appear. The function description for gtk_status_icon_new_from_icon_name states that "If the current icon theme is changed, the icon will be updated appropriately. Which to me, implies your code is not the only code that will access the X display, which could potentially be the problem.
我不使用托盘,所以我不能很容易地检查这个。我认为您对 gdk_threads_add_idle 使用锁保护 GTK/GDK API 是正确的。对我来说,没有任何明显的东西会导致这些消息出现。gtk_status_icon_new_from_icon_name 的函数描述指出“如果当前图标主题更改,图标将相应更新。对我而言,这意味着您的代码不是访问 X 显示的唯一代码,这可能是问题所在。
There is also some related info regarding XInitThreads() at
还有一些关于 XInitThreads() 的相关信息在
What is the downside of XInitThreads()?
Note that while GDK uses locks for the display, GTK/GDK do not ever call XInitThreads.
请注意,虽然 GDK 为显示使用锁,但 GTK/GDK 永远不会调用 XInitThreads。
On a side note: What's protecting the global variable "onclick", which is passed to execl after a fork(), The child will not inherit the parent's memory locks, and GLib mainloop is incompatible with fork(). Maybe you could copy the string to local variable.
旁注:什么保护全局变量“onclick”,它在 fork() 之后传递给 execl,子进程不会继承父进程的内存锁,并且 GLib 主循环与 fork() 不兼容。也许您可以将字符串复制到局部变量。
回答by ptomato
I'm not sure if bare pthreads are guaranteed to work with GTK. You should use the GThread wrappers.
我不确定是否保证裸 pthread 可以与 GTK 一起使用。您应该使用 GThread 包装器。
I think what the problem may be is that g_main_context_invoke()is adding set_icon()as an idle function. (It seems that that is what goes on behind the scenes, but I'm not sure.) Idle functions added using GLib's API, despite being executed on the main thread, need to hold the GDK lock. If you use the gdk_threads_add_idle()API (which is not deprecated) to invoke set_icon(), then everything should work properly with threading.
我认为问题可能g_main_context_invoke()是添加set_icon()为空闲功能。(这似乎是幕后发生的事情,但我不确定。)使用 GLib 的 API 添加的空闲函数尽管在主线程上执行,但需要持有 GDK 锁。如果您使用gdk_threads_add_idle()API(未弃用)来调用set_icon(),那么一切都应该与线程一起正常工作。
(Although this is just a wild guess.)
(虽然这只是一个疯狂的猜测。)
回答by Phillip Wood
As a work around, if you just want to avoid blocking the UI while waiting for some IO you could use the asynchronous IO from GIO. That would avoid you having to manage threads yourself.
作为一种解决方法,如果您只是想避免在等待某些 IO 时阻塞 UI,则可以使用来自GIO的异步 IO 。这将避免您必须自己管理线程。
Edit: Thinking about it you could just mark your file descriptors as non-blocking and add them as a source to the glib main loop and it will poll them for you in the main event loop without having to mess about with threads.
编辑:考虑一下,您可以将文件描述符标记为非阻塞并将它们作为源添加到 glib 主循环中,它会在主事件循环中为您轮询它们,而不必与线程混淆。
回答by barry day
You could avoid using threads by using gio_add_watch() which will invoke your callback function when there is data available on the channel.
您可以通过使用 gio_add_watch() 来避免使用线程,当通道上有可用数据时,它会调用您的回调函数。

