PyGTK, потоки и доступность - зависание приложения - PullRequest
1 голос
/ 10 августа 2011

Я создал несколько приложений, используя PyGTK и думал, что они были в порядке, пока я не выполнил их в среде с доступностью (GNOME в Ubuntu и Openbox в Debian).Я обнаружил, что они зависают , и что еще более неприятно - они вызывают зависание приложений AT-SPI.

Я создал репо со всеми вариантами использования PyGTK с потоками, о которых я мог подумать:

  • primarythread Префикс означает, что gtk.main () работает внутри основного потока (один выполняется первым Python),
  • вторичная нить префикс означает, что 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 происходит, например, после потери и восстановления фокуса его окном.

Проверка показала, что при первом запуске gtk.main () не возникает никаких проблем.основной поток Python.Но меня это не устраивает, так как мне не нравится рассматривать GUI как основную часть приложения.

Мои вопросы:

  1. Что-то не так с кодом в программах?помечены как вторичная тема ?Или это ошибка в GTK / GAIL / AT-SPI?
  2. Существует ли политика, запрещающая запуск gtk.main () вне первого / основного потока Python?

1 Ответ

0 голосов
/ 11 августа 2011

Единственная известная мне политика заключается в том, что только один поток может обращаться к библиотекам GTK в любое время.

Если вы хотите, чтобы графический интерфейс выполнялся в своем собственном вторичном потоке, вы должны быть уверены, что это единственный поток, обращающийся к GTK, поскольку GTK не является поточно-ориентированным.Вот почему в ваших вторичных случаях он работает только тогда, когда GTK импортируется из потока.Если он загружен вне потока, то технически основной поток также использует его в некоторой степени, и оба потока могут одновременно пытаться получить доступ к библиотеке. Причина, по которой ваши первичные сценарии работают, заключается в том, что вы правильно блокируете доступ кбиблиотеки GTK с gtk.gdk.threads_enter () и threads_leave ().Я думаю, что вы могли бы избавиться от них в файле secondthread_gtkgdk_import.py, и это сработает, поскольку есть только один поток, который знает о загрузке GTK.

Теперь это предположение с моей стороны, поскольку я незнаю много об AT-SPI.Так как вы уже вызываете условия гонки из двух ваших отдельных потоков в файлах virtualthread_gobject.py и secondthread_gtkgdk.py, AT-SPI также может каким-то образом зависеть от этого состояния.

Если вы действительно хотите, чтобы ваш GUI был во вторичном потоке, используйте файлthirthread_gtkgdk_import.py (возможно, удалив threads_enter () и threads_leave () как ненужные).Вместо этого я бы рекомендовал использовать графический интерфейс в качестве основного потока и запускать все фоновые процессы в дочерних потоках.


Сравните следующие два примера:

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 + в любой момент времени. ").

...