Проблемы резьбонарезной с GTK
Я строю довольно простой C приложение С помощью GTK, но должны выполнить некоторые блокировки ввода-вывода, которые вызовут обновления 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
is
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 должны выполняться в основном потоке, но это не так хорошо сочетается с блокировкой ввода-вывода, и я бы предпочел не создавать какой-то сложный механизм связи между потоками.
и Итак, мой вопрос в том, как я должен правильно справиться с этим?
EDIT: полный код можно увидеть здесь
EDIT2: как обновление, основанное на ответе @ptomato, я перешел в GThread
s и использование gdk_threads_add_idle()
как видно этой commit, но проблема все еще присутствует.
4 ответов
вызов XInitThreads()
. Это должно быть сделано до gtk_init
, это остановит сообщения!
что-то вроде этого:
#include <X11/Xlib.h>
...
XInitThreads();
...
gtk_init(&argc, &argv);
Я не помню, чтобы видел эти сообщения до GLIB 2.32, когда
g_thread_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 () at
в чем недостаток XInitThreads()?
обратите внимание, что в то время как GDK использует блокировки для дисплея, GTK / GDK никогда XInitThreads звонок.
на боковом примечании: что защищает глобальную переменную "onclick", которая передается execl после fork (), ребенок не наследует Родительский блокировки памяти и GLib mainloop несовместимы с fork (). Возможно, вы могли бы скопировать строку в локальную переменную.
Я не уверен, что голые pthreads гарантированно работают с GTK. Вы должны использовать обертки GThread.
Я думаю, что проблема может быть в том, что g_main_context_invoke()
добавить set_icon()
как функция простоя. (Кажется, именно это и происходит за кулисами, но я не уверен.) Функции простоя, добавленные с помощью API GLib, несмотря на выполнение в основном потоке, должны удерживать блокировку GDK. Если вы используете gdk_threads_add_idle()
API (который не устарел) для вызова set_icon()
, потом все должно правильно работайте с резьбой.
(хотя это просто предположение.)
как работа вокруг, если вы просто хотите избежать блокировки пользовательского интерфейса во время ожидания некоторого ввода-вывода, вы можете использовать асинхронный ввод-вывод из Джио. Это избавит вас от необходимости самостоятельно управлять потоками.
Edit: думая об этом, вы можете просто пометить свои файловые дескрипторы как неблокирующие и добавить их в качестве источника в основной цикл glib, и он будет опрашивать их для вас в основном цикле событий без необходимости возиться с потоками.
вы можете избежать использования потоков, используя gio_add_watch (), который вызовет вашу функцию обратного вызова, когда есть данные, доступные на канале.