GTK/Glib: как отправить сообщение из потока GUI в рабочий поток?

GTK не является потокобезопасным, но ориентирован на потоки — его можно использовать из нескольких потоков, гарантируя, что глобальная блокировка используется для защиты вызовов GTK API. Если мне нужно отправить сообщение из рабочего потока в поток графического интерфейса GTK, я просто вызываю gdk_threads_add_idle(), и указанный обратный вызов будет вызываться в потоке графического интерфейса через некоторое время.

Но как проще всего сделать обратное - вызвать указанный обратный вызов из потока non-GUI, когда пользователь нажимает кнопку?


person grigoryvp    schedule 16.09.2011    source источник
comment
Не могли бы вы предоставить более подробную информацию? Когда вы говорите обратный вызов, вы имеете в виду, что обработчик сигнала G(tk)Object должен запускаться из приложения non-GUI? Функция, добавленная через gdk_thread_add_idle(), срабатывает, когда в очереди событий нет ожидающих событий с более высоким приоритетом ... поэтому через некоторое время будет меняться до вызова функции :)   -  person another.anon.coward    schedule 16.09.2011
comment
@another Я хочу, чтобы мой код выполнялся отдельным пользователем, нажимающим кнопку графического интерфейса. Мне все равно, является ли это code функцией C, методом объекта GTK или чем-то еще. Конечно, я могу сделать это вручную, написав стандартный обработчик для события GTK, в этом случае поместив некоторое сообщение в мой собственный запрос и в отдельный поток, я могу использовать некоторый код для ожидания сообщения в очереди и вызвать некоторый обработчик для его обработки. Но это много кода и менее элегантно, чем gdk_threads_add_idle() :)   -  person grigoryvp    schedule 16.09.2011
comment
Когда вы говорите кнопку графического интерфейса, можно ли предположить, что это GtkWidget? AFAIK в GTK, если вам нужно обнаружить событие, например, нажатие пользователем кнопки графического интерфейса, это через механизм сигнализации; обратный вызов события запускается, чтобы указать, что конкретное событие произошло (может быть не мгновенным, о чем вы можете быть очень хорошо осведомлены). Таким образом, для нажатия кнопки у вас будет обратный вызов события, в котором вы будете выполнять необходимые операции (блокирующие/неблокирующие). У вас есть вопрос о создании потоков в обработчике событий? Или вы хотите узнать о нажатии кнопки без обработчика событий?   -  person another.anon.coward    schedule 17.09.2011
comment
@another Мне нужен самый простой способ выполнить event callback that indicates that the particular event has occurs в отдельном потоке без графического интерфейса. Я могу сделать это вручную, отправив сообщение в настраиваемую очередь, которую будет ждать мой поток, но это много кода. Так что, возможно, у GTK есть какой-то встроенный способ распространения сигнала в поток, не связанный с графическим интерфейсом.   -  person grigoryvp    schedule 17.09.2011
comment
С теми ограниченными знаниями, которые у меня есть, я не уверен, сможете ли вы это сделать, насколько я знаю, event callback будет вызываться в том же потоке графического интерфейса. Существует возможность создания нового сигнала через g_signal_new и передачи их через g_signal_emit, который вызовет обработчик сигнала, но я думаю, что он связан с GObject, и не уверен, что он будет служить вашей цели. Зачем нужна пользовательская очередь? Не может не-GUI ждать условных переменных? В event handler просто сигнализируйте потоку, ожидающему условия. Glib Threads (как и другие библиотеки потоков) имеют эти возможности   -  person another.anon.coward    schedule 17.09.2011
comment
В предыдущем комментарии я имел в виду, что если вы решите использовать Glib Threads в своем потоке без графического интерфейса, используйте g_cond_wait для условного ожидания, а при нажатии кнопки event callback on в потоке графического интерфейса просто сигнализируйте условие через g_cond_signal, чтобы поток без графического интерфейса доходы. Нет необходимости в пользовательских очередях. Возможно, вы уже знали об этом или могли подумать об этом, но на всякий случай я думаю, что это возможно :)   -  person another.anon.coward    schedule 17.09.2011
comment
@another Многие «асинхронные» действия запускаются из графического интерфейса и должны обрабатываться в отдельных потоках. Конечно, я могу использовать условное ожидание или очереди, но это будет много кода :(. Все, что я хочу найти, это какой-то способ уменьшить количество написанного кода. В конце концов, у GTK есть gdk_thread_add_idle(), который позволяет общаться из рабочего потока в поток графического интерфейса.Так что я блуждаю, это что-то так же просто, чтобы общаться из потока графического интерфейса в рабочий поток(и) :).   -  person grigoryvp    schedule 17.09.2011
comment
Это правда... Извините, если это вас беспокоит, но мне нужно немного больше ясности, чтобы увидеть, могу ли я предоставить некоторую информацию. Когда поток GUI запущен, рабочий поток уже создан и выполняет некоторые операции или рабочий поток создается только при нажатии кнопки? Если рабочий поток существует вместе с потоком графического интерфейса, блокируется ли он или ожидает, пока работает поток графического интерфейса; если он не блокируется, как поток графического интерфейса запрашивать работника для операций? Требуется ли графический интерфейс для отображения некоторой информации о ходе выполнения?   -  person another.anon.coward    schedule 18.09.2011
comment
Обычно рабочие потоки запускаются до GUI и просто ждут выполнения команды. Кроме того, это удобно, если рабочий поток уже существует и будет выполнять несколько команд одну за другой. Qt работает таким образом - вы просто запускаете поток, затем указываете Qt, что указанный объект живет внутри этого потока - и вуаля, теперь все события GUI, на которые подписан этот объект, будут обрабатываться одно за другим в указанная нить. Но я открыт для других возможностей - конечно, пул потоков, который порождает потоки для обработки событий графического интерфейса, не очень хорош, но C# каким-то образом справляется с этим во многих приложениях :).   -  person grigoryvp    schedule 18.09.2011
comment
Ах хорошо. К сожалению, я не работал с Qt, но с небольшой помощью Google я нашел moveToThread() в Qt; Я думаю, что это функция / функция, о которой вы упомянули в своем предыдущем комментарии, и ищете для этого альтернативу GTK. Если это так, то, насколько мне известно, я не знаю о такой возможности в GTK, но в Glib есть возможность пулов потоков, которые могут служить вашей цели. Возможно, вам удастся выполнить вашу задачу в отдельном пуле потоков.   -  person another.anon.coward    schedule 19.09.2011
comment
Пожалуйста, проверьте developer.gnome.org/glib/2.29/glib-Thread- Pools.html : ... reusing already started threads seems like a good idea. And it indeed is, but implementing this can be tedious and error-prone. Therefore GLib provides thread pools for your convenience. An added advantage is, that the threads can be shared between the different subsystems of your program, when they are using GLib. To create a new thread pool, you use g_thread_pool_new(). It is destroyed by g_thread_pool_free(). If you want to execute a certain task within a thread pool, you call g_thread_pool_push(). HTH   -  person another.anon.coward    schedule 19.09.2011
comment
Я прочитал все комментарии, и я должен что-то упустить, вам нужен метод для передачи сообщений между вашим графическим интерфейсом и вашим рабочим потоком, и вы не хотите реализовывать свою собственную очередь сообщений, потому что это много кода ? правильно ?. Почему бы не использовать это: GAsyncQueue, который уже реализован.   -  person erick2red    schedule 19.09.2011
comment
Ммм, я думаю, что вариант, предложенный erick2red, кажется хорошим для передачи сообщений в черно-белых потоках без особых проблем с кодированием ... Просто у меня не было возможности использовать все возможности, предоставляемые GLib, и, к сожалению, не использовал GAsyncQueue, поэтому я не знал... +1   -  person another.anon.coward    schedule 19.09.2011
comment
GAsyncQueue действительно хорошее решение. Это просто сложнее по сравнению с gdk_thread_add_idle(): мне нужно будет каким-то образом определить идентификаторы сообщений, и это будет БОЛЬШОЙ переключатель внутри потока, который вызовет соответствующий дескриптор функции для сообщения :). Для gdk_thread_add_idle() все это бесплатно :(.   -  person grigoryvp    schedule 24.09.2011


Ответы (1)


Ничто не мешает вам создать новый цикл без графического интерфейса с помощью g_main_loop_new, запустить его из потока без графического интерфейса с помощью g_main_loop_run и при необходимости вызвать g_idle_add из потока с графическим интерфейсом.

person nicola    schedule 29.09.2011
comment
Как я могу указать для g_idle_add, что он должен добавляться в очередь для моего собственного цикла без графического интерфейса, а не для цикла по умолчанию потока графического интерфейса по умолчанию? - person grigoryvp; 29.09.2011
comment
Вы должны написать свою собственную версию g_idle_add, которая прикрепляет ее к контексту другого цикла. Другими словами, напишите q копию этой функции:git.gnome .org/browse/glib/tree/glib/gmain.c#n4561, используя что-то вместо NULL. - person nicola; 30.09.2011