Consideremos un ejemplo para ver la tarea del TPL en acción. Supongamos que estás escribiendo una aplicación de consola Core .NET que procesará una imagen remota. Digamos que necesitas descargar una imagen de Internet, aplicarle un desenfoque y guardarla en el disco. Ahora bien, normalmente está bien que las aplicaciones de consola estén sincronizadas, pero digamos que quieres tener un tablero en tiempo real que se actualice constantemente con milisegundos, por ejemplo
123456while(!done){ Console.CursorLeft =0; Console.Write(System.DateTime.Now.ToString("HH:mm:ss.fff")); Thread.Sleep(50);}
csharp
Para que un tablero de mandos así se mantenga actualizado de forma fiable, necesitará que las operaciones de E/S y de manipulación de imágenes se realicen de forma asíncrona. Usando el TPL, puedes lograrlo realizando tales operaciones en métodos que devuelvan una tarea:
12345Tarea estática<byte[]};DownloadImage(string url){...}Tarea estática<byte[]};BlurImage(string imagePath){...}Tarea estáticaSalvarImage(byte[] bytes,string imagePath){...}
csharp
Fíjate en cómo la tarea puede tener un parámetro genérico T cuando quieres devolver algo para una tarea en particular. En este ejemplo, para ambos métodos se quiere devolver el conjunto de bytes de la imagen descargada o borrosa. En el caso de nuestro método SaveImage, los datos de la imagen se escriben en el disco y no se devuelve nada.
Ahora para la parte principal de nuestro código, donde llamamos a dichas funciones. Supongamos que estamos trabajando sólo con imágenes JPEG.
12345678910111213141516171819202122232425bool done =false;var url ="https://...jpg";var fileName = Path.GetFileName(url);DownloadImage(url).ContinueWith(task1 =>{var originalImageBytes = task1. Result;var originalImagePath = Path.Combine(ImageResourcesPath, fileName);SaveImage(originalImageBytes, originalImagePath).ContinueWith(task2 =>{BlurImage(originalImagePath). ContinueWith(task3 =>{var blurredImageBytes = task3.Result;var blurredFileName = $"{Path.GetFileNameWithoutExtension(fileName)}_blurred.jpg";var blurredImagePath = Path. Combine(ImageResourcesPath, blurredFileName);SaveImage(blurredImageBytes, blurredImagePath).ContinueWith(task4 ={{ done =true;});});});while(!done){/* actualizar el panel de control */}Console.WriteLine("Done!");
csharp
Fíjate que para cada tarea estamos añadiendo lo que se llama una continuación usando una función llamada ContinueWith. La continuación es una nueva tarea y es iniciada automáticamente por el TPL cuando la tarea precedente (es decir, la anterior) se completa. Así, hemos definido una cadena de acciones al principio, y el TPL monitoriza y coordina cuándo invocar cada acción. La ejecución de la aplicación continúa a través de las definiciones de las tareas rápidamente, procediendo al bucle while del tablero en la parte inferior. Dado que estamos realizando todas las operaciones costosas y latentes de forma asíncrona con una tarea, cada una de esas tareas puede tomar el tiempo que necesite sin afectar a las actualizaciones en tiempo real de nuestro tablero.
¿Significa eso que cada tarea se ejecuta en un hilo separado? Para saber realmente la respuesta a esa pregunta, tendríamos que mirar la implementación de los métodos DownloadImage, SaveImage y BlurImage. Dicho esto, la belleza de la abstracción de la Tarea significa que, para el propósito del código de llamada que hemos escrito aquí, no necesitamos saber.