2014-01-16 91 views
1

所以我應該在我的程序開始運行,使其線程安全(或線程感知正如我在一些地方已經閱讀):Python的GTK + 3安全線程

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()的存在。

回答

9

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

..但是,Gdk.threads_init() is deprecated,而且我recommmend到:

  • 不叫在所有
  • 使用GLib.idle_add Gdk.threads_init,Gdk.threads_enter /離開,而不是Gdk.threads_add_idle(或任何其他Gdk.threads_ *功能)
  • 推的東西撫摸GDK/GTK主線程使用GLib.idle/timeout_add

爲什麼?:

  • 不調用Gdk.threads_init意味着將沒有鎖,這是確定的,如果你從來沒有從另一個線程訪問GDK。
  • Gdk.threads_enter不做任何事情,因爲沒有鎖定。
  • 在這種情況下,GLib.idle_add等於Gdk.threads_add_idle

關於其他庫:

  • 某些GI模塊可以發出在其他線程某些信號/回調(GStreamer中GstPlayBin ::大約到完成信號例如);即使你根本沒有在你的代碼中使用Python線程。 Gdk/Gtk代碼不能直接在它們中調用,如果需要,也可以在那裏使用idle_add。
  • GLib/GStreamer的很多部分都是線程安全的,可以從其他線程調用。

TL; DR:只有GObject.threads_init()在線程推所有基於GTK/GDK代碼給主線程使用GLib.idle_add

+0

您發佈的鏈接使用'Gdk.threads_init()'。你能鏈接到提及它已被棄用的來源嗎?而且,你能解釋爲什麼'GLib.idle_add()'而不是'Gdk.threads_add_idle()'?謝謝。 – jpcgt

+0

@jpcgt更新.. – lazka

+0

@lazca我的應用程序沒有崩潰,因爲我實施了你的建議。爲此非常感謝。還有兩件事,使用'GObject.threads_init()'而不是'GLib.threads_init()'的基本原理是什麼?畢竟我們使用'GLib.add_idle()'與主線程同步。我讀過一些人說,最好使用並行進程而不是線程。你能評論嗎?再次感謝。 – jpcgt

0

這裏是一個必須讀取的原稿如果有人打算在多線程代碼中使用GTK。 https://wiki.gnome.org/Attic/GdkLock

這篇文檔確實幫助我理解了如何在單個進程中從C以及從python(通過PyGTK只是爲了在python中導入gtk)運行GTK。雖然GDK鎖可以通過XInitThreads()在Linux中避免,但它不是Windows的解決方案。像g_idle_add()g_timeout_add()這樣的功能是防止GUI粉碎的通用解決方案。然而,gdk_threads_enter()和gdk_thread_leave()還沒有完全無用。該文檔闡明瞭如果有人想要從不同線程或自定義事件處理程序或g_idle_add()回調更新GUI,如何安全地使用這些鎖。