Потоками в PyQt приложения: использование потоков в Qt или Python потоки?



Я пишу приложение с графическим интерфейсом, которое регулярно получает данные через веб-соединение. Поскольку это извлечение занимает некоторое время, это приводит к тому, что пользовательский интерфейс не реагирует во время процесса извлечения (его нельзя разделить на более мелкие части). Вот почему я хотел бы передать веб-соединение на аутсорсинг отдельному рабочему потоку.



[Да, я знаю, теперь у меня есть две проблемы.]



В любом случае, приложение использует PyQt4, поэтому я хотел бы знать, какой выбор лучше: использовать потоки Qt или использовать модуль Python threading? Каковы преимущества / недостатки каждого из них? Или у вас есть совершенно другое предложение?



Edit (re bounty): В то время как решение в моем конкретном случае, вероятно, будет использовать неблокирующий сетевой запрос, какДжефф обер иЛукаш Лалински (поэтому в основном оставляя проблемы параллелизма сетевой реализации), я все же хотел бы получить более глубокий ответ на общий вопрос:



Каковы преимущества а недостатки использования потоков PyQt4 (то есть Qt) над собственными потоками Python (из модуля threading)?





Edit 2: Спасибо всем за ваши ответы. Несмотря на то, что нет 100% согласия, кажется, существует широко распространенный консенсус, что ответ - "использовать Qt", так как преимущество этого-интеграция с остальной библиотекой, не вызывая никаких реальных недостатков.



Для тех, кто хочет выбрать между двумя потоковыми реализациями, я настоятельно рекомендую прочитать: все ответы приведены здесь, включая поток списка рассылки PyQt, на который ссылаетсяabbot .



Было несколько ответов, которые я обдумал для щедрости; в конце концов я выбрал abbot'S для очень уместной внешней ссылки; это было, однако, близко.

Еще раз спасибо.

836   7  

7 ответов:

Это было обсуждено не так давно в списке рассылки PyQt. Цитируя комментарии Джованни Баджо по этому вопросу:

В основном одно и то же. Главное отличие заключается в том, что QThreads лучше интегрирован с Qt (asynchrnous signals / slots, event loop и т. д.). Кроме того, вы не можете использовать Qt из потока Python (вы не можете, например сообщение события в основной поток через QApplication.postEvent): вы нужен QThread для этого, чтобы работать.

Общее правило thumb может быть использовать QThreads, если вы собираетесь как-то взаимодействовать с Qt, и использовать потоки Python в противном случае.

И некоторые более ранние комментарии на эту тему От автора PyQt: "они оба являются оболочками вокруг одних и тех же реализаций собственных потоков". И обе реализации используют GIL одинаково.

Потоки Python будут проще и безопаснее, и поскольку они предназначены для приложений на основе ввода-вывода, они могут обойти GIL. Тем не менее, рассматривали ли вы возможность неблокирующего ввода-вывода с использованием витых или неблокирующих сокетов/select?

EDIT: подробнее о потоках

Python threads

Потоки Python-это системные потоки. Однако Python использует глобальную блокировку интерпретатора (GIL), чтобы гарантировать, что интерпретатор всегда выполняет только определенный размер блока байт-кода инструкции по очереди. К счастью, Python выпускает GIL во время операций ввода-вывода, что делает потоки полезными для моделирования неблокирующего ввода-вывода

Важное предостережение: это может ввести в заблуждение, так как количество байт-кодовых инструкций не соответствует количеству строк в программе. Даже одно назначение не может быть атомарным в Python, поэтому мьютексная блокировка необходима для любого блока кода, который должен выполняться атомарно, даже с ГИЛ.

Qt threads

Когда Python передает управление скомпилированному модулю третьей стороны, он освобождает GIL. Модуль несет ответственность за обеспечение атомарности там, где это необходимо. Когда контроль будет передан обратно, Python будет использовать GIL. Это может сделать использование сторонних библиотек в сочетании с потоками запутанным. Еще сложнее использовать внешнюю потоковую библиотеку, поскольку она добавляет неопределенность относительно того, где и когда управление находится в руках модуля. переводчик.

Потоки QT работают с выпущенным GIL. Потоки QT могут выполнять код библиотеки QT (и другой скомпилированный код модуля, который не получает GIL) одновременно. Однако код Python, выполняемый в контексте потока QT , по-прежнему получает GIL, и теперь вам нужно управлятьдвумя наборами логики для блокировки вашего кода.

В конце концов, и Qt-потоки, и Python-потоки являются оболочками вокруг системных потоков. Python потоки являются несколько безопаснее использовать, так как те части, которые не написаны на Python (неявно используя GIL), используют GIL в любом случае (хотя оговорка выше все еще применима.)

Неблокирующий ввод-вывод

Потоки чрезвычайно усложняют ваше приложение. Особенно, когда речь идет об уже сложном взаимодействии между интерпретатором Python и скомпилированным кодом модуля. В то время как многие находят программирование на основе событий трудным для выполнения, основанный на событиях, неблокирующий ввод-вывод часто является гораздо менее трудно рассуждать, чем о нитях.

При асинхронном вводе-выводе вы всегда можете быть уверены, что для каждого открытого дескриптора путь выполнения последователен и упорядочен. Очевидно, что существуют проблемы, которые необходимо решить, например, что делать, когда код, зависящий от одного открытого канала, в дальнейшем зависит от результатов кода, который будет вызван, когда другой открытый канал возвращает данные.

Одним хорошим решением для событийного, неблокирующего ввода-вывода является новая библиотекаDiesel . Оно на данный момент он ограничен Linux, но чрезвычайно быстр и довольно элегантен.

Также стоит потратить время на изучение pyevent, оболочки вокруг замечательной библиотеки libevent, которая предоставляет базовую структуру для программирования на основе событий, используя самый быстрый доступный метод для вашей системы (определяется во время компиляции).

Преимущество QThread в том, что он интегрирован с остальной библиотекой Qt. То есть потоковые методы в Qt должны знать, в каком потоке они выполняются, а для перемещения объектов между потоками необходимо использовать QThread. Еще одна полезная функция-запуск собственного цикла событий в потоке.

Если вы обращаетесь к HTTP-серверу, вы должны рассмотреть QNetworkAccessManager.

Я задавал себе тот же вопрос, когда работал над PyTalk.

Если вы используете Qt, вам нужно использовать QThread, чтобы иметь возможность использовать фреймворк Qt и, в частности, систему сигнал/слот.

С помощью движка signal/slot вы сможете говорить от одного потока к другому и с каждой частью вашего проекта.

Кроме того, существует не очень вопрос производительности об этом выборе, так как оба являются привязками C++.

Вот мой опыт PyQt и нитка.

Я призываю вас использовать QThread.

У Джеффа есть несколько хороших моментов. Только один основной поток может делать любые обновления графического интерфейса. Если вам действительно нужно обновить графический интерфейс внутри потока, сигналы Qt-4 queued connection облегчают передачу данных между потоками и будут автоматически вызываться, если вы используете QThread; я не уверен, что они будут, если вы используете потоки Python, хотя легко добавить параметр в connect().

Я действительно не могу рекомендовать ни то, ни другое, но я могу попробовать описать различия между CPython и Qt-потоками.

Во-первых, потоки CPython не выполняются одновременно, по крайней мере, не в коде Python. Да, они действительно создают системные потоки для каждого потока Python, однако только поток, в настоящее время удерживающий глобальную блокировку интерпретатора, может быть запущен (расширения C и код FFI могут обойти его, но байт-код Python не выполняется, пока поток не содержит GIL).

С другой стороны, у нас есть Qt потоки, которые в основном являются общим слоем над системными потоками, не имеют глобальной блокировки интерпретатора и, таким образом, способны работать одновременно. Я не уверен, как PyQt справляется с этим, однако, если ваши потоки Qt не вызывают код Python, они должны быть в состоянии работать одновременно (бар различных дополнительных блокировок, которые могут быть реализованы в различных структурах).

Для дополнительной тонкой настройки можно изменить количество инструкций байт-кода, которые интерпретируются перед переключением права собственности на GIL-lower значения означают большее переключение контекста (и, возможно, более высокую скорость отклика), но меньшую производительность в каждом отдельном потоке (переключатели контекста имеют свою стоимость - если вы пытаетесь переключать каждые несколько инструкций, это не помогает ускорить.)

Надеюсь, что это поможет с вашими проблемами:)

Я не могу комментировать точные различия между потоками Python и PyQt, но я делаю то, что вы пытаетесь сделать с помощью QThread, QNetworkAcessManager и обязательно вызывайте QApplication.processEvents(), пока поток жив. Если отзывчивость GUI-это действительно проблема, которую вы пытаетесь решить, поможет более поздняя.

Comments

    Ничего не найдено.