gtk idle_add 不运行?
我有一个使用两个线程的应用程序:一个是图形界面(GUI),另一个是一些后台工作。我想给主线程发送请求,以便更新图形界面(比如移动进度条),但似乎没有效果。我把问题简化到了一个非常简单的例子:
import pygtk
pygtk.require('2.0')
import glib
import gtk
import threading
import sys
import time
def idle():
sys.stderr.write('Hello from another world.\n')
sys.stderr.flush()
gtk.main_quit()
def another_thread():
time.sleep(1)
glib.idle_add(idle)
thread = threading.Thread(target=another_thread)
thread.start()
gtk.main()
我原以为这段代码会从主线程/图形界面线程打印一些东西到标准错误输出,但什么都没发生。而且程序也没有退出,所以gtk.main_quit
这个函数没有被调用。
另外,往stderr
添加更多输出时,表现得也很奇怪。如果我把线程的函数改成:
sys.stderr.write('----\n')
sys.stderr.write('----\n')
sys.stderr.flush()
sys.stderr.write('After.\n')
sys.stderr.flush()
我看到输出中有1行,有时是2行。这看起来像是主线程进入gtk.main
时出现了一种竞争条件,但我不知道为什么会这样。
3 个回答
为什么不直接用 glib.timeout_add_seconds(1, idle)
并在 idle()
里返回 False
,而是要启动一个线程然后让它睡1秒呢?从另一个线程启动一个空闲函数其实是多余的,因为空闲函数本身就已经在另一个线程里运行了。
编辑:
我说“从另一个线程启动空闲函数是多余的”,是指你不需要启动一个空闲函数就能操作图形界面(GUI)和更新进度条。认为在其他线程中不能操作图形界面是个误解。
让我再说一下如何在其他线程中调用GTK的函数:
- 调用
glib.threads_init()
或gobject.threads_init()
,具体用哪个取决于你使用的库,正如vanza的回答中提到的。 - 调用
gtk.gdk.threads_init()
。我很确定你在回答中是对的,这个函数只需要在gtk.main()
之前调用。C文档建议在gtk_init()
之前调用,但如果我没记错的话,PyGTK在导入时就会调用这个函数。 - 把对
gtk.main()
的调用放在gtk.gdk.threads_enter()
和gtk.gdk.threads_leave()
之间。 把从以下地方调用GTK函数的代码放在:
- 主线程以外的线程
- 空闲函数
- 超时函数
- 基本上除了信号处理器以外的任何回调
也放在
gtk.gdk.threads_enter()
和gtk.gdk.threads_leave()
之间。
注意,除了用 enter
/leave
来包裹你的调用外,你也可以使用 with gtk.gdk.lock:
,然后在这个 with
块中进行调用。
这里有一些资源也解释了这个问题:
在多线程环境中使用glib之前,你需要先初始化glib的线程支持。只需要调用一下:
glib.threads_init()
然后再使用glib的函数。
以下内容对我来说是有效的:
在做任何事情之前,先调用一下 gtk.gdk.threads_init()
:在启动线程之前,或者在进入 gtk.main()
之前。我觉得实际上,这个函数只需要在进入 gtk.main()
之前调用就可以了,但在一切都很简单、只有一个线程的时候调用它也很方便。
在空闲回调函数中(示例中的 idle
),在处理GTK相关的内容之前,先调用 gtk.gdk.threads_enter()
(可以很简单地放在函数的开头),然后在函数结束之前调用 gtk.gdk.threads_leave()
。
调用 gtk.gdk.threads_init()
似乎还告诉 PyGTK 在休眠时不要占用全局解释器锁(GIL)——我觉得在示例中,我错过了一些来自辅助线程的输出,原因就是主线程在 gtk.main()
中休眠时仍然占用了 GIL。根据我的理解,gtk.gdk.threads_init()
为 PyGTK 和 GTK+ 带来了良好的效果。因此,即使你启动的线程不涉及 GTK+,也不处理 glib、gobject 等,只是进行基本的计算,调用 gtk.gdk.threads_init()
也是必要的。