PyGTK: gobject.idle_add() и timeout_add() с потоками
Есть ли какая-либо однозначная документация, указывающая, нужны ли блокировки (любого рода) или нет для idle_add() / timeout_add() и / или фактических обратных вызовов, установленных ими?
def work(*args):
# (1) gtk.gdk.threads_enter() #needed?
self.ui.change_some_label()
# (2) gtk.gdk.threads_leave() #?
# (3) gtk.gdk.threads_enter() #?
gobject.idle_add (work)
# (4) gtk.gdk.threads_leave() #?
def main():
gtk.gdk.threads_init()
#...
Нужны ли 1+2 и / или 3+4? Для каких версий pygtk это применимо? Я нацеливаюсь отдельно на 2.12 (на встроенной платформе) и 2.24 (на рабочем столе). Темы из-за gstreamer.
Для базовых функций C g_idle_add(), g_timeout_add() я нашел обсуждение gtk-app-devel, в котором говорится
Если вы вызвали gdk_threads_init, то обработчики простоя и тайм-аута будут работать без блокировки потока gdk, и вам придется самостоятельно добавлять вызовы gdk_threads_enter/ оставлять, если вы работаете с графическим интерфейсом.
... хотя это с 2004 года. Мне на удивление трудно найти четкую, конкретную документацию для GTK+-2 или для PyGTK.
Многие ответы на SO поддерживают планирование GUI через idle_add, без блокировок / критических секций (например, GUI не обновляется из другого потока при использовании PyGtk)
1 ответ
Призвание g_idle_add()
а также g_timeout_add()
не требует блокировки: они являются поточно-ориентированными операциями и гарантируют, что обратный вызов будет вызван в GMainContext
это в настоящее время вращается основной цикл.
документация, которую вы связали, говорит, что обратному вызову нужно будет получить мастер-блокировку GDK; обратный вызов вызывается GLib, но блокировка обеспечивается GDK, поэтому вам необходимо получить его явно, чтобы избежать прерывания потоков во время передачи обратного вызова.
по этой причине C API обеспечивает gdk_threads_add_idle()
а также gdk_threads_add_timeout()
функции (и их полные () варианты), которые гарантируют вызов вашего обратного вызова с удерживаемой блокировкой GDK. PyGTK не переносит эти функции, потому что он также должен был бы удерживать блокировку интерпретатора Python; это означает, что вам нужно будет не забыть позвонить gdk_threads_enter()
/gdk_threads_leave()
себя в обратном вызове.