Sincronizar hilos y GUI en una aplicación Delphi

Multi-threading en Delphi le permite crear aplicaciones que incluyen varias rutas simultáneas de ejecución.

Una aplicación normal de Delphi tiene un solo subproceso, lo que significa que todos los objetos VCL acceden a sus propiedades y ejecutan sus métodos dentro de este único subproceso. Para acelerar el procesamiento de datos en su aplicación, incluya uno o más hilos secundarios.

Subprocesos del procesador

UN hilo es un canal de comunicación desde una aplicación a un procesador. Los programas de subproceso único necesitan comunicación para fluir en ambas direcciones (hacia y desde el procesador) a medida que se ejecuta; Las aplicaciones multiproceso pueden abrir varios canales diferentes, lo que acelera la ejecución.

Hilos y GUI

Cuando se ejecutan varios subprocesos en la aplicación, surge la pregunta de cómo puede actualizar su interfaz gráfica de usuario como resultado de la ejecución de un subproceso. La respuesta está en la clase TThread Sincronizar método.

Para actualizar la interfaz de usuario de su aplicación, o hilo principal, desde un hilo secundario, debe llamar al método Sincronizar. Esta técnica es un método seguro para subprocesos que evita conflictos de subprocesos múltiples que pueden surgir al acceder a propiedades de objetos o métodos que no son seguros para subprocesos, o al usar recursos que no están en el subproceso principal de ejecución.

A continuación se muestra un ejemplo de demostración que utiliza varios botones con barras de progreso, cada barra de progreso muestra el "estado" actual de la ejecución del hilo.

unidad MainU;
interfaz
usos
Windows, Mensajes, SysUtils, Variantes, Clases, Gráficos, Controles, Formularios,
Diálogos, ComCtrls, StdCtrls, ExtCtrls;
tipo
// clase interceptor
TButton = clase (StdCtrls.TButton)
OwnedThread: TThread;
ProgressBar: TProgressBar;
final;
TMyThread = clase (TThread)
privado
FCounter: entero;
FCountTo: Integer;
FProgressBar: TProgressBar;
FOwnerButton: TButton;
procedimiento DoProgress;
procedimiento SetCountTo (valor constante: entero);
procedimiento SetProgressBar (valor constante: TProgressBar);
procedimiento SetOwnerButton (const Valor: TButton);
protegido
procedimiento Ejecutar; anular;
público
constructor Create (CreateSuspended: Boolean);
propiedad CountTo: Entero leer FCountTo escribir SetCountTo;
propiedad ProgressBar: TProgressBar lee FProgressBar escribe SetProgressBar;
propiedad OwnerButton: TButton lee FOwnerButton escribe SetOwnerButton;
final;
TMainForm = clase (TForm)
Botón1: TButton;
ProgressBar1: TProgressBar;
Botón2: TButton;
ProgressBar2: TProgressBar;
Botón 3: TButton;
ProgressBar3: TProgressBar;
Botón 4: TButton;
ProgressBar4: TProgressBar;
Botón5: TButton;
ProgressBar5: TProgressBar;
procedimiento Button1Click (Remitente: TObject);
final;
var
MainForm: TMainForm;
implementación
$ R * .dfm
TMyThread
constructor TMyThread.Create (CreateSuspended: Boolean);
empezar
heredado;
FCounter: = 0;
FCountTo: = MAXINT;
final;
procedimiento TMyThread.DoProgress;
var
PctDone: Extendido;
empezar
PctDone: = (FCounter / FCountTo);
FProgressBar.Position: = Round (FProgressBar.Step * PctDone);
FOwnerButton.Caption: = FormatFloat ('0.00%', PctDone * 100);
final;
procedimiento TMyThread.Execute;
const
Intervalo = 1000000;
empezar
FreeOnTerminate: = True;
FProgressBar.Max: = FCountTo div Interval;
FProgressBar.Step: = FProgressBar.Max;
mientras FCounter < FCountTo do
empezar
si FCounter mod Interval = 0 entonces Synchronize (DoProgress);
Inc (FCounter);
final;
FOwnerButton.Caption: = 'Inicio';
FOwnerButton.OwnedThread: = nil;
FProgressBar.Position: = FProgressBar.Max;
final;
procedimiento TMyThread.SetCountTo (const Value: Integer);
empezar
FCountTo: = Valor;
final;
procedimiento TMyThread.SetOwnerButton (const Valor: TButton);
empezar
FOwnerButton: = Valor;
final;
procedimiento TMyThread.SetProgressBar (const Value: TProgressBar);
empezar
FProgressBar: = Valor;
final;
procedimiento TMainForm.Button1Click (remitente: TObject);
var
aButton: TButton;
aThread: TMyThread;
aProgressBar: TProgressBar;
empezar
aButton: = TButton (remitente);
si no está asignado (aButton.OwnedThread) entonces
empezar
aThread: = TMyThread.Create (True);
aButton.OwnedThread: = aThread;
aProgressBar: = TProgressBar (FindComponent (StringReplace (aButton.Name, 'Button', 'ProgressBar', [])));
aThread.ProgressBar: = aProgressBar;
aThread.OwnerButton: = aButton;
aThread.Resume;
aButton.Caption: = 'Pausa';
final
más
empezar
si aButton.OwnedThread.Suspendded entonces
aButton.OwnedThread.Resume
más
aButton.OwnedThread.Suspend;
aButton.Caption: = 'Ejecutar';
final;
final;
final.

Gracias a Jens Borrisholt por enviar este ejemplo de código..