Estoy escribiendo una aplicación GUI que recupera datos con regularidad a través de una conexión web. Dado que esta recuperación lleva un tiempo, esto hace que la interfaz de usuario no responda durante el proceso de recuperación (no se puede dividir en partes más pequeñas). Es por eso que me gustaría subcontratar la conexión web a un hilo de trabajo separado.
[Sí, lo sé, ahora tengo dos problemas ].
De todos modos, la aplicación usa PyQt4, así que me gustaría saber cuál es la mejor opción: ¿Usar los hilos de Qt o usar el threading
módulo Python ? ¿Cuáles son las ventajas / desventajas de cada uno? ¿O tienes una sugerencia totalmente diferente?
Editar (recompensa): si bien la solución en mi caso particular probablemente será usar una solicitud de red sin bloqueo como sugirieron Jeff Ober y Lukáš Lalinský (así que básicamente dejar los problemas de concurrencia a la implementación de la red), todavía me gustaría más respuesta en profundidad a la pregunta general:
¿Cuáles son las ventajas y desventajas de usar subprocesos de PyQt4 (es decir, Qt) sobre subprocesos nativos de Python (del threading
módulo)?
Edición 2: Gracias a todos por sus respuestas. Aunque no hay un acuerdo del 100%, parece haber un consenso generalizado de que la respuesta es "usar Qt", ya que la ventaja de eso es la integración con el resto de la biblioteca, sin causar desventajas reales.
Para cualquiera que busque elegir entre las dos implementaciones de subprocesos, les recomiendo que lean todas las respuestas proporcionadas aquí, incluido el hilo de la lista de correo de PyQt al que abbot se vincula.
Había varias respuestas que consideré para la recompensa; al final elegí abad's como referencia externa muy relevante; sin embargo, estuvo cerca.
Gracias de nuevo.
fuente
QCoreApplication.postEvent
desde un hilo de Python a una velocidad de 100 veces por segundo, en una aplicación que se ejecuta en varias plataformas y ha sido probada durante miles de horas. Nunca he visto ningún problema por eso. Creo que está bien siempre que el objeto de destino esté ubicado en MainThread o QThread. También lo envolví en una bonita biblioteca, vea qtutils .Los subprocesos de Python serán más simples y seguros, y dado que son para una aplicación basada en E / S, pueden omitir el GIL. Dicho esto, ¿ha considerado E / S sin bloqueo utilizando sockets / select Twisted o sin bloqueo?
EDITAR: más sobre hilos
Hilos de Python
Los hilos de Python son hilos del sistema. Sin embargo, Python utiliza un bloqueo de intérprete global (GIL) para garantizar que el intérprete solo ejecute un bloque de cierto tamaño de instrucciones de código de bytes a la vez. Afortunadamente, Python libera el GIL durante las operaciones de entrada / salida, lo que hace que los subprocesos sean útiles para simular E / S sin bloqueo.
Advertencia importante: esto puede ser engañoso, ya que el número de instrucciones de código de bytes no se corresponde con el número de líneas de un programa. Incluso una sola asignación puede no ser atómica en Python, por lo que es necesario un bloqueo mutex para cualquier bloque de código que deba ejecutarse atómicamente, incluso con GIL.
Hilos QT
Cuando Python cede el control a un módulo compilado de terceros, libera el GIL. Es responsabilidad del módulo garantizar la atomicidad cuando sea necesario. Cuando se devuelve el control, Python usará el GIL. Esto puede hacer que el uso de bibliotecas de terceros junto con subprocesos sea confuso. Es aún más difícil usar una biblioteca de subprocesos externa porque agrega incertidumbre sobre dónde y cuándo el control está en manos del módulo frente al intérprete.
Los hilos QT operan con el GIL liberado. Los subprocesos QT pueden ejecutar el código de la biblioteca QT (y otro código de módulo compilado que no adquiere el GIL) al mismo tiempo. Sin embargo, el código Python ejecutado dentro del contexto de un hilo QT aún adquiere el GIL, y ahora debe administrar dos conjuntos de lógica para bloquear su código.
Al final, tanto los subprocesos de QT como los de Python son envoltorios de los subprocesos del sistema. Los subprocesos de Python son marginalmente más seguros de usar, ya que aquellas partes que no están escritas en Python (implícitamente usando el GIL) usan el GIL en cualquier caso (aunque la advertencia anterior todavía se aplica).
E / S sin bloqueo
Los subprocesos añaden una complejidad extraordinaria a su aplicación. Especialmente cuando se trata de la ya compleja interacción entre el intérprete de Python y el código del módulo compilado. Si bien a muchos les resulta difícil seguir la programación basada en eventos, la E / S sin bloqueo basada en eventos suele ser mucho menos difícil de razonar que los hilos.
Con E / S asincrónicas, siempre puede estar seguro de que, para cada descriptor abierto, la ruta de ejecución es coherente y ordenada. Obviamente, hay cuestiones que deben abordarse, como qué hacer cuando el código que depende de un canal abierto depende además de los resultados del código que se llamará cuando otro canal abierto devuelva datos.
Una buena solución para E / S sin bloqueo basadas en eventos es la nueva biblioteca Diesel . Está restringido a Linux en este momento, pero es extraordinariamente rápido y bastante elegante.
También vale la pena dedicar tiempo a aprender pyevent , un envoltorio de la maravillosa biblioteca libevent, que proporciona un marco básico para la programación basada en eventos utilizando el método más rápido disponible para su sistema (determinado en el momento de la compilación).
fuente
La ventaja
QThread
es que está integrado con el resto de la biblioteca Qt. Es decir, los métodos con reconocimiento de subprocesos en Qt necesitarán saber en qué subproceso se ejecutan y, para mover objetos entre subprocesos, deberá usarlosQThread
. Otra característica útil es ejecutar su propio bucle de eventos en un hilo.Si está accediendo a un servidor HTTP, debería considerarlo
QNetworkAccessManager
.fuente
QNetworkAccessManager
parece prometedora. Gracias.Me hice la misma pregunta cuando trabajaba en PyTalk .
Si está usando Qt, necesita usar
QThread
para poder usar el marco Qt y especialmente el sistema de señal / ranura.Con el motor de señal / ranura, podrás hablar de un hilo a otro y con cada parte de tu proyecto.
Además, no hay muchas dudas sobre el rendimiento sobre esta elección, ya que ambos son enlaces de C ++.
Aquí está mi experiencia con PyQt y thread.
Te animo a usar
QThread
.fuente
Jeff tiene algunos buenos puntos. Solo un hilo principal puede realizar actualizaciones de GUI. Si necesita actualizar la GUI desde dentro del hilo, las señales de conexión en cola de Qt-4 facilitan el envío de datos a través de hilos y se invocarán automáticamente si está utilizando QThread; No estoy seguro de si lo serán si usa subprocesos de Python, aunque es fácil agregar un parámetro a
connect()
.fuente
Realmente no puedo recomendar tampoco, pero puedo intentar describir las diferencias entre los hilos CPython y Qt.
En primer lugar, los subprocesos de CPython no se ejecutan al mismo tiempo, al menos no el código Python. Sí, crean subprocesos del sistema para cada subproceso de Python, sin embargo, solo se permite que se ejecute el subproceso que actualmente tiene el bloqueo de intérprete global (las extensiones C y el código FFI pueden omitirlo, pero el código de bytes de Python no se ejecuta mientras el subproceso no contiene GIL).
Por otro lado, tenemos subprocesos Qt, que son básicamente capas comunes sobre subprocesos del sistema, no tienen bloqueo de intérprete global y, por lo tanto, son capaces de ejecutarse simultáneamente. No estoy seguro de cómo PyQt lo maneja, sin embargo, a menos que sus subprocesos Qt llamen al código Python, deberían poder ejecutarse simultáneamente (excluya varios bloqueos adicionales que podrían implementarse en varias estructuras).
Para un ajuste más fino, puede modificar la cantidad de instrucciones de código de bytes que se interpretan antes de cambiar la propiedad de GIL; los valores más bajos significan más cambio de contexto (y posiblemente una mayor capacidad de respuesta) pero un menor rendimiento por hilo individual (los cambios de contexto tienen su costo, si intente cambiar cada pocas instrucciones que no ayuden a acelerar).
Espero que te ayude con tus problemas :)
fuente
No puedo comentar sobre las diferencias exactas entre los hilos de Python y PyQt, pero yo he estado haciendo lo que estás tratando de hacer uso de
QThread
,QNetworkAcessManager
y asegurándose de llamadaQApplication.processEvents()
, mientras que el hilo está vivo. Si la capacidad de respuesta de la GUI es realmente el problema que está tratando de resolver, lo último lo ayudará.fuente
QNetworkAcessManager
no requiere un hilo oprocessEvents
. Utiliza operaciones de E / S asincrónicas.QNetworkAcessManager
yhttplib2
. Mi código asincrónico usahttplib2
.