Pregunta

Mantener la interfaz gráfica de usuario receptiva mientras la aplicación realiza un gran procesamiento de la CPU es uno de los desafíos de la programación efectiva de la interfaz gráfica de usuario.

Aquí hay una buena discusión sobre cómo hacer esto en wxPython. Para resumir, hay 3 formas:

  1. Usar hilos
  2. Use wxYield
  3. Divida el trabajo y hágalo en el controlador de eventos IDLE

¿Qué método has encontrado que sea el más efectivo? Las técnicas de otros marcos (como Qt, GTK o API de Windows) también son bienvenidas.

¿Fue útil?

Solución

Hilos. Es lo que siempre busco porque puedes hacerlo en cualquier marco que necesites.

Y una vez que estás acostumbrado a los subprocesos múltiples y al procesamiento paralelo en un idioma / marco, eres bueno en todos los marcos.

Otros consejos

Definitivamente hilos. ¿Por qué? El futuro es multi-core. Casi cualquier nueva CPU tiene más de un núcleo o, si tiene solo uno, podría admitir el hipervínculo y, por lo tanto, fingir que tiene más de uno. Para hacer un uso eficaz de las CPU de múltiples núcleos (e Intel está planeando llegar a 32 cores en un futuro no muy lejano), necesita múltiples subprocesos. Si ejecuta todo en un subproceso principal (por lo general, el subproceso de la interfaz de usuario es el subproceso principal), los usuarios tendrán CPU con 8, 16 y un día 32 núcleos y su aplicación nunca usa más de uno de ellos, IOW se ejecuta mucho, mucho más lento de lo que podría correr.

Actual si planea una aplicación hoy en día, me gustaría alejarme del diseño clásico y pensar en una relación maestro / esclavo. Su interfaz de usuario es el maestro, la única tarea es interactuar con el usuario. Eso es mostrar datos al usuario y recopilar información del usuario. Siempre que su aplicación necesite " procesar cualquier dato " (incluso cantidades pequeñas y grandes mucho más importantes), cree una " tarea " de cualquier tipo, reenvíe esta tarea a un subproceso en segundo plano y haga que el subproceso realice la tarea, brindando retroalimentación a la interfaz de usuario (por ejemplo, cuánto ha completado o si la tarea aún se está ejecutando, por lo que la interfaz de usuario puede mostrar un " ; indicador de trabajo en progreso "). Si es posible, divida la tarea en muchas subtareas pequeñas e independientes y ejecute más de un proceso en segundo plano, alimentando una subtarea a cada una de ellas. De esa manera, su aplicación puede beneficiarse realmente de los núcleos múltiples y ser más rápida cuanto más CPU tienen los núcleos.

En realidad, compañías como Apple y Microsoft ya están planeando cómo hacer que sus UIs de un solo hilo sean multihebra. Incluso con el enfoque anterior, puede que algún día tenga la situación de que la interfaz de usuario es el propio cuello de botella. Los procesos en segundo plano pueden procesar datos mucho más rápido de lo que la IU puede presentarlos al usuario o pedirle al usuario una entrada. Hoy en día, muchos marcos de IU son poco seguros para subprocesos, muchos no son seguros para subprocesos, pero eso cambiará. El procesamiento en serie (hacer una tarea después de otra) es un diseño en extinción, el procesamiento en paralelo (hacer muchas tareas a la vez) es el futuro. Basta con mirar a los adaptadores gráficos. Incluso la tarjeta NVidia más moderna tiene un rendimiento lamentable, si nos fijamos en la velocidad de procesamiento en MHz / GHz solo de la GPU. ¿Cómo puede ser mejor que la CPU fuera de los cálculos 3D? Simple: en lugar de calcular un punto de polígono o un píxel de textura tras otro, calcula muchos de ellos en paralelo (en realidad, un montón al mismo tiempo) y de esa manera alcanza un rendimiento que aún hace que las CPUs lloren. P.ej. ¡El ATI X1900 (también para nombrar al competidor) tiene 48 unidades de sombreado!

Creo que delayedresult es lo que está buscando:

http://www.wxpython.org/docs /api/wx.lib.delayedresult-module.html

Consulte la demostración de wxpython para ver un ejemplo.

Subprocesos o procesos según la aplicación. A veces, en realidad es mejor que la GUI sea su propio programa y simplemente enviar llamadas asíncronas a otros programas cuando tenga trabajo por hacer. Seguirá teniendo varios subprocesos en la GUI para monitorear los resultados, pero puede simplificar las cosas si el trabajo que se realiza es complejo y no está directamente conectado a la GUI.

Hilos - Usemos una vista simple de 2 capas (GUI, lógica de aplicación).

El trabajo de lógica de la aplicación se debe hacer en un hilo de Python separado. Para los eventos asíncronos que necesitan propagarse hasta la capa GUI, use el sistema de eventos de wx para publicar eventos personalizados. Publicar eventos wx es seguro para subprocesos por lo que posiblemente podría hacerlo desde múltiples contextos.

Trabajando en la otra dirección (eventos de entrada de GUI que activan la lógica de la aplicación), he encontrado que lo mejor es lanzar un sistema de eventos personalizado. Use el módulo Queue para tener una forma segura de subprocesos y objetos emergentes de eventos. Luego, para cada función miembro síncrona, combínela con una versión asíncrona que empuje el objeto de la función de sincronización y los parámetros en la cola de eventos.

Esto funciona particularmente bien si solo se puede realizar una sola operación a nivel de lógica de la aplicación a la vez. El beneficio de este modelo es que la sincronización es simple: cada función síncrona funciona dentro de su propio contexto de manera secuencial de principio a fin, sin preocuparse por la preferencia o el rendimiento codificado a mano. No necesitarás cerraduras para proteger tus secciones críticas. Al final de la función, publique un evento en la capa GUI que indique que la operación ha finalizado.

Podría escalar esto para permitir que existan múltiples subprocesos a nivel de aplicación, pero volverán a aparecer las preocupaciones habituales con la sincronización.

edit : olvidé mencionar que lo mejor de esto es que es posible desacoplar completamente la lógica de la aplicación del código GUI. La modularidad ayuda si alguna vez decide utilizar un marco diferente o una versión de línea de comandos de la aplicación. Para hacer esto, necesitará un distribuidor de eventos intermedio (nivel de aplicación - > GUI) implementado por la capa GUI.

Trabajando con Qt / C ++ para Win32.

Dividimos las principales unidades de trabajo en diferentes procesos. La GUI se ejecuta como un proceso independiente y puede enviar / recibir datos del " worker " Procesos según sea necesario. Funciona bien en el mundo actual de varios núcleos.

Esta respuesta no se aplica a la pregunta del OP con respecto a Python, sino que es más bien una meta-respuesta.

La manera fácil es hilos. Sin embargo, no todas las plataformas tienen subprocesos preventivos (por ejemplo, BREW, algunos otros sistemas integrados) Si es posible, simplemente fragmenta el trabajo y hazlo en el controlador de eventos IDLE.

Otro problema con el uso de subprocesos en BREW es que no limpia los objetos de la pila de C ++, por lo que es demasiado fácil perder memoria si simplemente mata el subproceso.

Uso subprocesos para que el bucle de eventos principal de la GUI nunca se bloquee.

Para algunos tipos de operaciones, usar procesos separados tiene mucho sentido. De vuelta en el día, generando un proceso incurrió en una gran cantidad de gastos generales. Con el hardware moderno, esta sobrecarga apenas es un problema en la pantalla. Esto es especialmente cierto si está generando un proceso de larga ejecución.

Una ventaja (discutible) es que es un modelo conceptual más simple que los hilos que podrían conducir a un código más mantenible. También puede hacer que su código sea más fácil de probar, ya que puede escribir scripts de prueba que ejercitan estos procesos externos sin tener que involucrar a la GUI. Algunos incluso podrían argumentar que esa es la principal ventaja.

En el caso de algún código en el que trabajé una vez, el cambio de subprocesos a procesos separados dio lugar a una reducción neta de más de 5000 líneas de código y, al mismo tiempo, hace que la GUI sea más sensible, el código más fácil de mantener y probar. todo mientras se mejora el rendimiento general total.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top