Demostrando diferentes formas de usar hilos en Delphi

Demasiados usuarios de Delphi cometen el error de pensar que un hilo es algún tipo de magia que mejorará el rendimiento de su aplicación. Desafortunadamente, esto está lejos de ser cierto. El error número 1 más grande al intentar implementar un hilo es hacer que acceda directamente a los controles visuales de la aplicación. Pero estos controles visuales solo pueden funcionar en el contexto del hilo principal de la aplicación. El uso de otro hilo para actualizar los controles en la interfaz de usuario debe planificarse e implementarse con mucho cuidado. Y en la mayoría de los casos, probablemente no sea la solución correcta para el problema.
En pocas palabras, el marco VCL de Delphi no es seguro. Si bien hay muchas formas de integrar un hilo en su interfaz de usuario, no hay una solución única para todos. Siempre variará dependiendo de lo que esté tratando de lograr. La mayoría de las veces, la gente quiere lograr un mejor rendimiento (velocidad). Pero eso rara vez se hace al usar un hilo. En cambio, los escenarios más comunes en los que un hilo se integra en una interfaz de usuario es mantener esa interfaz de usuario que responde durante una tarea larga.
Para esto, imaginaremos una aplicación simple con un solo botón que descarga un archivo de Internet cuando se haga clic. La aplicación ya tiene un hilo principal que se utiliza para toda la interfaz de usuario. En la plataforma de Windows, esto significa enviar/recibir mensajes de Windows, atraer a un lienzo de control, reconocer la interacción del usuario, etc. Este hilo es esencialmente un bucle gigante que está girando muy rápido. Para cada revolución de este hilo giratorio, se ejecutan ciertas piezas de código.
En un solo entorno roscado, esta descarga de archivos bloquearía este bucle de Spinning, hasta que la descarga esté terminada. Durante este tiempo, este hilo ya no puede realizar actualizaciones de la interfaz de usuario, detectar los clics del usuario o cualquier cosa. Esto es lo que hace que Windows ponga (no responda) en el título de tales formas, porque, bueno, tal como dice, no está respondiendo.
Aquí es donde entran hilos adicionales. Debe responder a Windows. En lugar de bloquear el hilo de la interfaz de usuario principal con esta descarga de archivos gigantes, podría poner esa descarga de archivo en otro hilo. Es así de simple, ¿verdad?
Puede preguntarse "¿Cómo monitoreo el progreso?" o "¿Cómo me notifican cuando se hace?" Esto significaría que el hilo de descarga debe interactuar de alguna manera con el hilo principal. Aquí es exactamente donde entra la confusión. Un hilo no puede simplemente interferir con otro hilo, porque no se sabe en qué punto un hilo está realmente. Ahora hay dos bucles separados, y cuando desea actualizar la interfaz de usuario, ese hilo de la interfaz de usuario podría estar en cualquier lugar haciendo cualquier cosa. Lo más importante, suponga que el hilo de la interfaz de usuario principal está en el proceso de escribir una cadena para la misma propiedad de control que su otro hilo también quiere escribir. Ahora tiene dos hilos que intentan escribir en la misma dirección de memoria, lo que puede dar lugar a problemas impredecibles.
Por sincronización. La clase Tthread de Delphi tiene un método sincronizado () que permite que un hilo interactúe con el hilo de la interfaz de usuario principal solo en un momento en el que realmente se comportará correctamente, cuando realmente espera tal ocurrencia. El código que se sincroniza de otro hilo en realidad no se ejecuta en el contexto de ese hilo: siempre se ejecuta en el contexto del hilo de la interfaz de usuario principal. Esa es la idea de sincronizar () es ejecutar código en el hilo de la interfaz de usuario.
Entonces, al final, en realidad no usas el VCL del hilo. En su lugar, su hilo envía una señal al hilo principal, y solo cuando el hilo principal esté listo, ejecutará ese código. Mientras tanto, su hilo secundario se bloquea mientras espera a que termine el hilo principal.
Luego está el error de pensar que una gran operación de UI estaría mejor en un hilo. Supongamos que tiene una lista donde desea completar millones de artículos. Por supuesto, eso llevará tiempo, y durante este tiempo su solicitud no responderá. De nuevo. Así que solo mueve ese código a un hilo, ¿verdad?
Nuevamente, cualquier interacción de la interfaz de usuario debe realizarse desde el hilo principal, y solo el hilo principal. Los subprocesos son útiles si necesita realizar cálculos largos, procesar cantidades masivas de datos, esperar una respuesta de un recurso remoto o cualquier cosa que consuma mucho tiempo y no esté directamente relacionada con la interfaz de usuario.
Eso es difícil de decir. Pero hay una práctica común que se recomienda altamente al escribir un hilo: coloque el código de su subproceso en una unidad propia. Esta unidad debe aislarse de cualquier otra unidad de IU. Ni siquiera debe tener ninguna unidad relacionada con VCL en su cláusula de usos. El hilo ni siquiera debería saber cómo se está utilizando. Debe ser esencialmente un muñeco, con el único propósito de realizar su larga tarea. Cuando se trata de actualizaciones de UI desde un hilo, esto se logra mejor por eventos sincronizados.
Exactamente cómo suena. Es un evento que está sincronizado, como se explicó anteriormente. Un evento es simplemente un puntero a un procedimiento que puede asignar al hilo antes de que comience. Dentro del hilo, cuando necesite actualizar la interfaz de usuario, usaría sincronize () para activar este evento. Con este diseño, el hilo nunca tendría que saber que incluso está siendo utilizado por una interfaz de usuario. Al mismo tiempo, también logra inadvertidamente la abstracción. El hilo se vuelve reutilizable. Puede conectarlo a algún otro proyecto que quizás ni siquiera tenga una interfaz de usuario (digamos un servicio de Windows).
Aquí hay algunos enlaces directos a recursos relacionados sobre la seguridad de los subprocesos de VCL, en caso de que no quiera buscar ...
Esta aplicación demuestra el uso de hilos en Delphi. Dado que hay muchas cosas diferentes que saber, se dividen en diferentes secciones para diferentes fines. Cada tema tiene al menos 1 unidad de formulario (incrustada en una hoja de pestaña), y al menos 1 unidad independiente que contiene su funcionalidad aparte de la interfaz de usuario. Esto se hace a propósito, para mostrar que los hilos deben aislarse de cualquier interfaz de usuario.
La forma principal en sí no tiene ninguna lógica. Todo lo que hace es incrustar los formularios en pestañas. En el controlador de eventos FormCreate() , realiza numerosas llamadas a EmbedForm() que instancia un formulario para cada hoja de pestaña.
En realidad, usar la aplicación es muy simple. Simplemente navegue a una de las pestañas, y cada una tendrá sus propias instrucciones.


Muestra cómo se puede descargar un archivo de Internet en un hilo. Hay una única función universal definida DownloadFile() que realiza la descarga. La interfaz de usuario tiene 3 botones:
De manera predeterminada, la URL que se descargará es un archivo de prueba proporcionado por ThinkBroadband.com, pero puede usar cualquier URL que desee. También puede elegir la ubicación para guardar el archivo. Esta es una demostración muy simple, por lo que el nombre de archivo/extensión del nombre de archivo local debe ajustarse a sus necesidades: no cambiará automáticamente para la URL que está descargando (como suelen hacerlo los navegadores).

Muestra cómo actualizar una barra de progreso de un hilo que está realizando una tarea larga.

Demuestra el uso de una conexión de base de datos dentro de un hilo y sincronizando datos al hilo de la interfaz de usuario.

Demuestra múltiples hilos que consumen ciclos de CPU masivos para cargar la prueba de su procesador.