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


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

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

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

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

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


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

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

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

7 96

7 ответов:

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

Это в основном то же самое. Основное отличие заключается в том, что QThreads лучше интегрирован с Qt (асинхронные сигналы / слоты, цикл событий и т. д.). Кроме того, вы не можете использовать Qt из потока Python (вы не можете, например сообщение события в основной поток через QApplication.после мероприятия): вы нужен QThread для этого, чтобы работать.

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

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

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

EDIT: more on threads

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 и скомпилированным кодом модуля. Хотя многие считают, что программирование на основе событий трудно следовать, основанный на событиях, неблокирующий ввод-вывод часто гораздо менее сложно рассуждать, чем потоки.

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

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

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

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

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

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

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

С сигналом/слот двигателя, вы сможете говорить из потока в другой и с каждой частью вашего проекта.

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

здесь мой опыт PyQt и thread.

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

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

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

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

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

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

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

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