PyGTK,线程和可访问性 - 应用程序挂起

时间:2011-08-10 11:25:39

标签: multithreading gtk accessibility pygtk

我已经使用 PyGTK 创建了一些应用程序,并认为它们一直没问题,直到我在启用辅助功能的环境中执行它们(Ubuntu上的GNOME和Debian上的Openbox) 。我发现他们挂起,更令人沮丧的是 - 他们导致AT-SPI应用程序挂起。

我创建了一个 repo ,其中包含使用PyGTK的所有变体以及我能想到的线程:

  • primarythread 前缀表示gtk.main()在主线程内部工作 (首先由Python执行),
  • secondarythread 前缀意味着gtk.main()在辅助线程内工作,
  • 多处理前缀表示导入,gtk.main()在另一个进程中执行。

  • gobject 后缀表示只使用了gobject.threads_init(),

  • gtkgdk 后缀表示已使用gobject和gtk.gdk.threads_init(),
  • import 后缀表示在新主题内执行“import gtk”。

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作为应用程序的主要部分。

我的问题是:

  1. 标记为 secondarythread 的程序中的代码有什么问题吗?或者它是GTK / GAIL / AT-SPI中的错误?
  2. 是否有禁止在第一个/主要Python线程外运行gtk.main()的策略?

1 个答案:

答案 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 +。“)。