Ir al contenido

Delphi Multithreading Moderno: Escriba Código Asíncrono Limpio y sin Complicaciones

Promesa Cumplida

En 2025, durante mis charlas en Embarcadero Conference, Luso Delphi y CodeRage, terminé mi presentación con unas diapositivas “misteriosas” (Diapositivas 17 y 18).

Hablé sobre el viaje del desarrollador: salimos de la complejidad manual de TThread, evolucionamos a la eficiencia de la PPL (Parallel Programming Library), pero aún faltaba algo.

Quienes usan PPL intensivamente conocen el problema. Para orquestar pipelines complejos (descargar datos -> procesar -> guardar -> actualizar UI), terminamos cayendo en la temida “Pyramid of Doom”: anidamiento excesivo de métodos anónimos, bloques try..except repetitivos y llamadas manuales a TThread.Queue para no congelar la pantalla.

En esa diapositiva, prometí una visión de futuro: Una interfaz fluida donde el código asíncrono pudiera leerse como una frase.

La espera ha terminado. La promesa se ha cumplido.

Hoy, presento oficialmente Dext.Threading.Async — la implementación real de ese concepto, ahora disponible en Dext Framework.

El Problema: La “Deuda” de la Complejidad

Sección titulada «El Problema: La “Deuda” de la Complejidad»

En mi libro, “Delphi Multithreading”, dedico capítulos enteros a enseñar cómo proteger sus hilos, evitar Race Conditions y garantizar que las excepciones no derriben su aplicación silenciosamente. Esta base teórica es innegociable.

Sin embargo, en el día a día, escribir toda esta infraestructura “a mano” para cada botón de su sistema es agotador. Vea el patrón tradicional:

// La forma estándar de usar PPL
TTask.Run(procedure
begin
try
var Datos := DescargarBytes;
// Anidamiento...
TThread.Synchronize(nil, procedure
begin
ActualizarUI(Datos); // ¿Y si el Form ya se cerró? Access Violation.
end);
except
on E: Exception do ManejarError(E); // Boilerplate repetitivo
end;
end);

Comparative Concept

Con el nuevo motor de Dext, abstraemos la complejidad de ITask, IFuture y el manejo de excepciones en una sintaxis declarativa.

¿Vamos a un ejemplo real? Imagine un Dashboard que necesita cargar 3 paneles pesados simultáneamente al abrir la pantalla. Si lo hiciéramos secuencialmente, el usuario esperaría la suma de los tiempos. Con Dext, disparamos en paralelo y el código permanece limpio:

uses Dext.Threading.Async;
procedure TFormDashboard.LoadDashboard;
begin
// 1. Carga el Perfil del Usuario (I/O Bound)
// Dispara y olvida la complejidad. Dext gestiona el hilo.
var Task1 := TAsyncTask.Run<TUserProfile>(
function: TUserProfile
begin
// Ejecuta en Background Thread Pool
Result := UserService.GetProfile(Self.UserId);
end)
.ThenBy(procedure(User: TUserProfile)
begin
// Ejecuta procesamiento adicional en background si es necesario
LogAcceso(User);
end)
.OnComplete(procedure(User: TUserProfile)
begin
// Ejecuta AUTOMÁTICAMENTE en el Main Thread (seguro para VCL/FMX)
UserLabel.Caption := 'Bienvenido, ' + User.Name;
imgAvatar.Bitmap := User.Avatar;
end)
.OnException(procedure(E: Exception)
begin
// Manejo centralizado de errores en el Main Thread
ShowMessage('Error al cargar perfil: ' + E.Message);
end)
.Start; // Inicia la ejecución
// 2. Carga Gráfico Financiero (CPU Bound)
// Ejecuta en PARALELO al anterior
var Task2 := TAsyncTask.Run<TFinancialData>(
function: TFinancialData
begin
// Verifica cancelación automáticamente antes de comenzar
Result := FinanceService.CalculateHeavyMetrics(CurrentYear);
end)
.OnComplete(procedure(Data: TFinancialData)
begin
ChartSales.Series[0].Add(Data.Values);
end)
.Start;
// 3. Actualiza Notificaciones
var Task3 := TAsyncTask.Run<Integer>(
function: Integer
begin
Result := NotificationService.CountUnread;
end)
.OnComplete(procedure(Count: Integer)
begin
Badge.Text := Count.ToString;
Badge.Visible := Count > 0;
end)
.Start;
// Espera a que terminen todas las tareas
TAsyncTask.WaitForAll([Task1, Task2, Task3]);
end;

¿Qué ganamos aquí?

  1. Legibilidad: Leemos el código de arriba a abajo, como una historia.
  2. Seguridad de Hilo: OnComplete y OnException ya garantizan la sincronización con el Main Thread o hilo llamador. Se acabó el TThread.Queue disperso por el código.
  3. Propagación de Excepción: Si GetProfile falla, el flujo salta el ThenBy y cae directo en OnException. Sin try..except contaminando la regla de negocio.

¿Pero y si el usuario cierra el formulario mientras estas 3 tareas se están ejecutando? ¿Tendremos un Access Violation al intentar actualizar el UserLabel?

Es aquí donde entra la teoría que enseño en el Capítulo 4 del libro: Cancelación Cooperativa.

Dext implementa el patrón ICancellationToken (inspirado en .NET) de forma transparente. Puede pasar un token a la tarea, y la cadena fluida verifica ese token antes de cada paso.

// Vincula el Token al ciclo de vida del Form
FTokenSource := TCancellationTokenSource.Create;
TAsyncTask.Run(...)
.WithCancellation(FTokenSource.Token) // La magia sucede aquí
.OnComplete(...)
.Start;
procedure TFormDashboard.FormDestroy(Sender: TObject);
begin
// Al destruir el form, cancela todo lo que está pendiente.
// Dext impide que el OnComplete (que accede a componentes visuales) se ejecute.
FTokenSource.Cancel;
end;

Esto resuelve uno de los errores más comunes y difíciles de rastrear en aplicaciones Delphi Multithread: el callback que intenta acceder a un objeto destruido.

El Dext Framework entrega la herramienta lista para producción. Pero para usar multithreading con maestría, necesita entender lo que realmente está sucediendo. ¿Por qué el Pool de Hilos es mejor que crear Hilos manuales? ¿Qué es Context Switching? ¿Cómo funcionan las Primitivas de Sincronización?

Delphi Multithreading Book

Es por eso que digo que el Libro y el Framework forman un par perfecto:

Cumplí mi promesa hecha en CodeRage. Fluent Tasks ya no es solo una diapositiva de PowerPoint; es código real que puede usar hoy para eliminar la complejidad de su proyecto.

Delphi Multithreading Book 3D

Lo invito a descargar Dext, probar Dext.Threading.Async y, si quiere dominar la ingeniería detrás de esto, asegurar su copia del libro en mi sitio.

Documentación Async API: https://github.com/cesarliws/dext/blob/main/Docs/async-api.md

Descargue Dext: https://github.com/cesarliws/dext

Asegure el Libro: https://www.cesarromero.com.br/

¡Vamos a programar (en paralelo)! 🚀

#Delphi #Multithreading #DelphiMultithreadingBook #DextFramework #AsyncProgramming #CodeRage #SoftwareArchitecture #OpenSource