Python GTK+ 3 安全线程编程

6

那么,在我的程序开头,我应该运行什么来使其线程安全(或者像我在某些地方读到的那样,线程感知):

from gi.repository import Gtk, Gdk, GLib, GObject
import threading

GLib.threads_init()     # ?
GObject.threads_init()  # YES!
Gdk.threads_init()      # ?

my_app()

def my_threaded_func():
   Glib.idle_add(lambda: some_gui_action())
   Glib.timeout_add(300, lambda: some_gui_action())

t = threading.Thread(target=my_thread_func)
t.daemon = True
t.start()

Gtk.main()

那么,在我的线程中应该做什么?需要某种锁吗?使用Python的线程库是否安全,还是应该使用GLib、GObject或Gdk中的某些内容?我知道有大量的问题/答案/示例存在,但它们彼此矛盾,不适用于Gtk+ 3,或者不适用于Python,甚至我认为官方文档Python GI(http://lazka.github.io/pgi-docs/)甚至没有提到GObject.threads_init()和Gdk.threads_init()的存在。

2个回答

17

https://wiki.gnome.org/Projects/PyGObject/Threading

但是,Gdk.threads_init()已经被弃用了,我建议:

  • 不要使用Gdk.threads_init、Gdk.threads_enter/leave。
  • 使用GLib.idle_add代替Gdk.threads_add_idle(或任何其他Gdk.threads_*函数)。
  • 使用GLib.idle/timeout_add将涉及Gdk/Gtk的内容推到主线程中。

为什么?

  • 不调用Gdk.threads_init意味着没有锁,如果你从另一个线程访问GDK,则没有问题。
  • Gdk.threads_enter因为没有锁所以没有作用。
  • 在这种情况下,GLib.idle_add等同于Gdk.threads_add_idle

关于其他库:

  • 一些GI模块可以在其他线程中发出某些信号/回调(例如,在GStreamer中,GstPlayBin :: about-to-finish信号);即使您的代码中根本没有使用Python线程。如果需要,在那里也必须使用idle_add来调用Gdk/Gtk代码。
  • GLib/GStreamer的许多部分是线程安全的,可以从其他线程中调用。

总结:仅在线程中使用GObject.threads_init(),将所有Gtk / Gdk代码推到主线程中使用GLib.idle_add。


你发布的链接使用了Gdk.threads_init()。你能否提供一个源,说明它已经被弃用了?另外,你能解释一下为什么要使用GLib.idle_add()而不是Gdk.threads_add_idle()吗?谢谢。 - jpcgt
@lazca自从我实施了你的建议,我的应用程序就没有崩溃过。非常感谢你。还有两件事,使用GObject.threads_init()而不是GLib.threads_init()的理由是什么?毕竟我们正在使用GLib.add_idle()与主线程同步。我读到有人说最好使用并行进程而不是线程。你能发表一下评论吗?再次感谢。 - jpcgt
(GLib.threads_init is GObject.threads_init) == True。这只是一个别名...正如PyGObject维基页面所指出的那样,它与GObject或GLib无关,但仍然因历史原因而存在。您可以将其视为gi.threads_init()。正如维基页面上也指出的那样,自3.10.2以来不再需要它。 - lazka

2
如果有人要在多线程代码中使用GTK,那么这是一份必读的文件。点击链接可以查看:https://wiki.gnome.org/Attic/GdkLock 这篇文章帮助我了解了如何在C语言以及通过PyGTK在python中(只需导入gtk)在单个进程中运行GTK。虽然在Linux中可以通过XInitThreads()避免GDK锁定,但这并不是Windows的解决方案。像g_idle_add()g_timeout_add()这样的函数是防止GUI崩溃的通用解决方案。然而,gdk_threads_enter()gdk_thread_leave()也并非完全无用。该文档澄清了如何安全地使用这些锁,以便从不同的线程、自定义事件处理程序或g_idle_add()回调中更新GUI。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接