我已经使用 PyGTK 创建了一些应用程序,并认为它们一直没问题,直到我在启用辅助功能的环境中执行它们(Ubuntu上的GNOME和Debian上的Openbox) 。我发现他们挂起,更令人沮丧的是 - 他们导致AT-SPI应用程序挂起。
我创建了一个 repo ,其中包含使用PyGTK的所有变体以及我能想到的线程:
多处理前缀表示导入,gtk.main()在另一个进程中执行。
gobject 后缀表示只使用了gobject.threads_init(),
showApps.py是使用AT-SPI列出启用了辅助功能的应用程序的应用程序示例。
我总结了下表中的测试(也在README文件中):
"Hang" column indicates if the GTK application has hung.
"Listed" column indicates if the application is visible in the AT-SPI
listing.
| hang | listed |
----------------------------------+----------+--------+
primarythread_gobject.py | no | yes |
primarythread_gtkgdk.py | no | yes |
secondarythread_gobject_import.py | no [1] | yes |
secondarythread_gobject.py | yes | hang |
secondarythread_gtkgdk_import.py | no [1] | yes |
secondarythread_gtkgdk.py | yes | hang |
multiprocessing_gobject.py | no | yes |
[1] ** (secondarythread_gobject_import.py:5828): CRITICAL **:
giop_thread_request_push: assertion `tdata != NULL' failed
-- at the application termination
当AT-SPI标记为“挂起”时,它会在应用程序列表期间无条件挂起。
挂起PyGTK应用程序会在丢失并通过其窗口重新获得焦点后发生。
测试表明,从第一个/主要Python线程运行gtk.main()时不会出现问题。但这并不满足我,因为我不喜欢将GUI作为应用程序的主要部分。
我的问题是:
答案 0 :(得分:0)
我所知道的唯一策略是任何时候只有一个线程可以访问GTK库。
如果您希望GUI在其自己的辅助线程中运行,那么您需要确保这是唯一访问GTK的线程,因为GTK不是线程安全的。这就是为什么在你的二线程情况下,它只适用于从线程中导入GTK的情况。如果它是在线程之外加载的,那么从技术上讲,主线程也在某种程度上使用它,并且两个线程都可以尝试同时访问库..你的primarythread案例工作的原因是你正确锁定访问权限GTK库使用gtk.gdk.threads_enter()和threads_leave()。我认为你可以在secondarythread_gtkgdk_import.py中删除它们并且它会工作,因为只有一个线程知道GTK已被加载。
现在,这是我的猜测,因为我对AT-SPI了解不多。由于您已经在secondarythread_gobject.py和secondarythread_gtkgdk.py中的两个独立线程中引起了竞争条件,因此AT-SPI也可能以某种方式受到此状态的影响。
如果您确实希望在辅助线程中使用GUI,请使用secondarythread_gtkgdk_import.py(可能将threads_enter()和threads_leave()删除为不必要的)。我建议改为将GUI作为主线程,并在子线程中启动所有后台进程。
比较以下两个例子:
import threading
import time
class mythread(threading.Thread):
def __init__(self):
super(mythread, self).__init__()
def run(self):
print time.ctime()
t = mythread()
t.run()
print time.ctime()
$ python2 test.py
Mon Aug 15 23:12:41 2011
Mon Aug 15 23:12:41 2011
import threading
class mythread(threading.Thread):
def __init__(self):
super(mythread, self).__init__()
def run(self):
import time
print time.ctime()
t = mythread()
t.run()
print time.ctime()
$ python2 test.py
Mon Aug 15 23:12:46 2011
Traceback (most recent call last):
File "test.py", line 15, in <module>
print time.ctime()
NameError: name 'time' is not defined
在第一种情况下,从主线程导入time
,然后生成子进程。子进程也可以访问time
,因此时间打印两次。在第二种情况下,在子线程中导入time
。但是,父线程看不到这一点,因此对time.ctime()
的第二次调用失败。
当你从子线程加载GTK时,父线程不知道它,所以你不会遇到两个线程试图访问GTK库的问题(引用GDK文档:“也就是说,只有一个线程可以在任何给定时间使用GTK +。“)。