Проблемы резьбонарезной с 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, я перешел в GThreads и использование 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 (), который вызовет вашу функцию обратного вызова, когда есть данные, доступные на канале.