Multihilo en Delphi: Uso de API de Windows y TThread

El multihilo es una técnica fundamental para el desarrollo de aplicaciones que requieren ejecutar varias tareas simultáneamente. En entornos como Delphi, existen dos enfoques principales: utilizar directamente la API de Windows (CreateThread) o emplear la clase TThread proporcionada por el propio framework. Ambos métodos permiten ejecutar código en paralelo al hilo principal de la aplicación, evitando que la interfaz de usuario se bloquee durante operaciones prolongadas.

Un problema común al realizar procesamientos intensivos en el hilo principal es la congelación de la ventana. Por ejemplo, si se ejecuta un bucle de gran iteración para actualizar un control visual, la aplicación dejará de responder a eventos del usuario. Una solución temporal es llamar a Application.ProcessMessages dentro del bucle, lo que permite procesar mensajes pendientes y mantener la UI responsive. Sin embargo, esto no constituye un verdadero multihilo, ya que el código sigue ejecutándose en el mismo hilo y cualquier interaccción del usuario puede interrumpir el bucle.

Antes de implementar hilos, es recomendable utilizar bloqueos (Lock/Unlock) en recursos compartidos como el lienzo de dibujo (Canvas). Esto prepara el código para un entorno multihilo y previene condiciones de carrera. A continuación se muestra un ejemplo de uso de bloqueo:

function EjecutarCalculos: Integer;
var
  iter: Integer;
begin
  for iter := 0 to 400000 do
  begin
    FormPrincipal.Lienzo.Lock;
    FormPrincipal.Lienzo.TextOut(20, 30, IntToStr(iter));
    FormPrincipal.Lienzo.Unlock;
  end;
  Result := 0;
end;

Para crear un hilo mediante la API de Windows, se utiliza la función CreateThread. Esta requiere una función con firma específica como punto de entrada. El siguiente código ilustra su uso:

function RutinaHilo(param: Pointer): Integer; stdcall;
var
  idx: Integer;
begin
  for idx := 0 to 400000 do
  begin
    FormPrincipal.Lienzo.Lock;
    FormPrincipal.Lienzo.TextOut(20, 30, IntToStr(idx));
    FormPrincipal.Lienzo.Unlock;
  end;
  Result := 0;
end;

procedure TFormPrincipal.BotonLanzarClic(Sender: TObject);
var
  idHilo: THandle;
begin
  CreateThread(nil, 0, @RutinaHilo, nil, 0, idHilo);
end;

Alternativamente, se puede heredar de la clase TThread y sobrescribir su método Execute. Esto encapsula la lógica del hilo dentro de un objeto:

type
  THiloCalculo = class(TThread)
  protected
    procedure Execute; override;
  end;

procedure THiloCalculo.Execute;
var
  n: Integer;
begin
  FreeOnTerminate := True;
  for n := 0 to 400000 do
  begin
    FormPrincipal.Lienzo.Lock;
    FormPrincipal.Lienzo.TextOut(20, 30, IntToStr(n));
    FormPrincipal.Lienzo.Unlock;
  end;
end;

procedure TFormPrincipal.BotonTThreadClic(Sender: TObject);
begin
  THiloCalculo.Create(False);
end;

La API CreateThread admite varios parámetros para controlar el comportamiento del hilo, como el tamaño de la pila, atributos de seguridad y opciones de arranque. Si se establece el indicador CREATE_SUSPENDED, el hilo se crea en estado suspendido y debe reanudarse explícitamente con ResumeThread. Las funciones SuspendThread y ResumeThread permiten controlar la ejecución del hilo mediante un contader de suspensión.

Para sincronizar el acceso a recursos compartidos entre múltiples hilos, se pueden utilizar secciones críticas. Delphi ofrece la clase TCriticalSection en la unidad SyncObjs, que simplifica la exclusión mutua:

uses SyncObjs;

var
  SeccionCritica: TCriticalSection;

procedure TFormPrincipal.FormCreate(Sender: TObject);
begin
  SeccionCritica := TCriticalSection.Create;
end;

procedure TFormPrincipal.FormDestroy(Sender: TObject);
begin
  SeccionCritica.Free;
end;

function RutinaHiloSincronizado(param: Pointer): DWORD; stdcall;
var
  i: Integer;
begin
  SeccionCritica.Enter;
  try
    for i := 0 to 99 do
      Lista.Items.Add(IntToStr(i));
  finally
    SeccionCritica.Leave;
  end;
  Result := 0;
end;

Finalmente, la función WaitForSingleObject permite esperar la finalización de un hilo o evento. Su uso es común cuando un hilo necesita esperar a que otro termine antes de continuar. Un ejemplo simple es esperar el cierre de un proceso externo:

function EsperarFinalizacion(hProceso: THandle): DWORD; stdcall;
begin
  if WaitForSingleObject(hProceso, INFINITE) = WAIT_OBJECT_0 then
    FormPrincipal.Caption := 'Proceso finalizado';
  Result := 0;
end;

Etiquetas: Delphi multihilo CreateThread TThread sincronización

Publicado el 6-21 01:21