【问题标题】:PyGTK, threads and accessibility - application hangsPyGTK、线程和可访问性 - 应用程序挂起
【发布时间】:2011-08-10 11:25:38
【问题描述】:

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

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

  • primarythread 前缀表示 gtk.main() 在主线程内工作 (首先由 Python 执行),
  • secondarythread 前缀表示 gtk.main() 在辅助线程内工作,
  • multiprocessing前缀表示import和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() 的策略?

【问题讨论】:

    标签: multithreading gtk accessibility pygtk


    【解决方案1】:

    我知道的唯一政策是任何时候只有一个线程可以访问 GTK 库。

    如果您希望 GUI 在其自己的辅助线程中运行,那么您需要确保这是访问 GTK 的唯一线程,因为 GTK 不是线程安全的。这就是为什么在您的辅助线程情况下,它仅在从线程内导入 GTK 时才有效。如果它是在线程之外加载的,那么从技术上讲,主线程也在某种程度上使用它,并且两个线程可以尝试同时访问该库。您的主线程案例工作的原因是因为您正确锁定了对带有 gtk.gdk.threads_enter() 和 threads_leave() 的 GTK 库。我认为你可以在 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+。”)。

    【讨论】:

    • 抱歉,您所写的内容不合逻辑,并且可能部分不正确。首先,我认为需要定义同时使用某些东西。我认为,当我们同时谈论使用资源时,我们谈论的是读取和写入共享资源。我不考虑在两个线程中声明同时使用的东西。
    • 如果假设导入模块会将模块“锁定”到线程或 Python 线程与 GTK 线程完全解耦,则应在文档中提及。首先,您写道问题是两个线程正在使用该库。比你提到的用于这个特定场合的进入和离开功能。要使用它们,我必须导入 GTK。我也不知道你在哪里注意到了比赛条件。从外部到 GTK 的通信应该只通过 gtk_main。当它被执行时,主线程已经完成了与 GTK 的交互。
    • 关于您的第一条评论,我不是编造的:developer.gnome.org/gdk/stable/gdk-Threads.html
    • 关于您的第二条评论,我希望我添加的示例有助于澄清我的意思。
    • 但你只引用了句子的一部分。这完全改变了那段的意思。完整的段落: >>>GTK+ 是“线程感知的”但不是线程安全的——它提供了一个由 gdk_threads_enter()/gdk_threads_leave() 控制的全局锁,它保护了 GTK+ 的所有使用。也就是说,在任何给定时间,只有一个线程可以使用 GTK+。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-13
    • 2014-09-13
    • 1970-01-01
    • 2010-12-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多