Я пишу приложение с графическим интерфейсом, которое регулярно извлекает данные через веб-соединение. Поскольку это извлечение занимает некоторое время, это приводит к тому, что пользовательский интерфейс не отвечает во время процесса извлечения (его нельзя разделить на более мелкие части). Вот почему я хотел бы передать веб-соединение отдельному рабочему потоку.
[Да, я знаю, теперь у меня две проблемы .]
В любом случае, приложение использует PyQt4, поэтому я хотел бы знать, что лучше: использовать потоки Qt или использовать threading
модуль Python ? Каковы преимущества / недостатки каждого? Или у вас есть совсем другое предложение?
Изменить (повторное вознаграждение): хотя решение в моем конкретном случае, вероятно, будет использовать неблокирующий сетевой запрос, как предложили Джефф Обер и Лукаш Лалински (так что в основном оставляя проблемы параллелизма для сетевой реализации), я все же хотел бы больше подробный ответ на общий вопрос:
Каковы преимущества и недостатки использования потоков PyQt4 (т.е. Qt) над собственными потоками Python (из threading
модуля)?
Изменить 2: Спасибо всем за ответы. Хотя нет 100% согласия, похоже, существует широко распространенное мнение, что ответ - «используйте Qt», поскольку преимуществом этого является интеграция с остальной частью библиотеки, при этом не вызывая реальных недостатков.
Для тех, кто хочет выбрать между двумя реализациями потоков, я настоятельно рекомендую прочитать все ответы, представленные здесь, включая ветку списка рассылки PyQt, на которую ссылается аббат .
Я подумал о нескольких ответах на награду; в конце концов, я выбрал аббата для очень релевантной внешней ссылки; Однако это был серьезный вызов.
Еще раз спасибо.
источник
QCoreApplication.postEvent
из потока Python со скоростью 100 раз в секунду в приложении, которое работает на нескольких платформах и было протестировано в течение 1000 часов. Я никогда не видел от этого никаких проблем. Я думаю, это нормально, если целевой объект находится в MainThread или QThread. Я также завернул его в красивую библиотеку, см. Qtutils .Потоки Python будут проще и безопаснее, и, поскольку они предназначены для приложений на основе ввода-вывода, они могут обходить GIL. Тем не менее, рассматривали ли вы неблокирующий ввод-вывод с использованием Twisted или неблокирующих сокетов / select?
РЕДАКТИРОВАТЬ: подробнее о потоках
Потоки Python
Потоки Python - это системные потоки. Однако Python использует глобальную блокировку интерпретатора (GIL), чтобы гарантировать, что интерпретатор одновременно выполняет только блок инструкций байтового кода определенного размера. К счастью, Python выпускает GIL во время операций ввода / вывода, делая потоки полезными для моделирования неблокирующего ввода / вывода.
Важное предупреждение: это может ввести в заблуждение, поскольку количество инструкций байт-кода не соответствует количеству строк в программе. Даже одно присвоение не может быть атомарным в Python, поэтому блокировка мьютекса необходима для любого блока кода, который должен выполняться атомарно, даже с GIL.
QT потоки
Когда 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
.источник
QNetworkAccessManager
выглядит многообещающим. Спасибо.Я задавал себе тот же вопрос, когда работал с PyTalk .
Если вы используете Qt, вам нужно использовать,
QThread
чтобы иметь возможность использовать структуру Qt и особенно систему сигналов / слотов.С движком сигналов / слотов вы сможете общаться из потока с другим и с любой частью вашего проекта.
Более того, этот выбор не вызывает большого сомнения в производительности, поскольку оба являются привязками C ++.
Вот мой опыт работы с PyQt и потоком.
Я рекомендую вам использовать
QThread
.источник
У Джеффа есть хорошие моменты. Только один основной поток может выполнять любые обновления графического интерфейса. Если вам действительно нужно обновить графический интерфейс из потока, сигналы подключения Qt-4 в очереди упрощают отправку данных между потоками и будут автоматически вызываться, если вы используете QThread; Я не уверен, будут ли они, если вы используете потоки Python, хотя добавить параметр в
connect()
.источник
Я тоже не могу рекомендовать, но я могу попытаться описать различия между потоками CPython и Qt.
Прежде всего, потоки CPython не выполняются одновременно, по крайней мере, код Python. Да, они действительно создают системные потоки для каждого потока Python, однако разрешено запускать только поток, в настоящее время содержащий глобальную блокировку интерпретатора (расширения C и код FFI могут обходить его, но байт-код Python не выполняется, пока поток не содержит GIL).
С другой стороны, у нас есть потоки Qt, которые в основном являются общим слоем для системных потоков, не имеют глобальной блокировки интерпретатора и, следовательно, могут работать одновременно. Я не уверен, как PyQt справляется с этим, однако, если ваши потоки Qt не вызывают код Python, они должны иметь возможность работать одновременно (за исключением различных дополнительных блокировок, которые могут быть реализованы в различных структурах).
Для дополнительной тонкой настройки вы можете изменить количество инструкций байт-кода, которые интерпретируются перед переключением владения GIL - более низкие значения означают большее переключение контекста (и, возможно, более высокую скорость отклика), но меньшую производительность для каждого отдельного потока (переключение контекста имеет свою стоимость - если вы попробуйте переключать каждые несколько инструкций, это не помогает ускорить.)
Надеюсь, это поможет с вашими проблемами :)
источник
Я не могу комментировать точные различия между потоками Python и PyQt, но я делал то, что вы пытаетесь сделать
QThread
,QNetworkAcessManager
и обязательно вызываю,QApplication.processEvents()
пока поток жив. Если проблема, которую вы пытаетесь решить, действительно связана с быстродействием графического интерфейса, то последнее поможет.источник
QNetworkAcessManager
не требует потока илиprocessEvents
. Он использует асинхронные операции ввода-вывода.QNetworkAcessManager
иhttplib2
. Мой асинхронный код используетhttplib2
.