Cómo: Encadenar varias tareas con continuaciones

En la biblioteca TPL, una tarea cuyo método ContinueWith se invoca se denomina tarea antecedente y la tarea que se define en el método ContinueWith se denomina continuación. En este ejemplo se muestra cómo usar los métodos ContinueWith y ContinueWith de las clases Task y Task<TResult> para especificar una tarea que se inicia después de que finalice su tarea antecedente.

También muestra cómo especificar una continuación que solo se ejecuta si la tarea antecedente se cancela.

En estos ejemplos se muestra cómo continuar desde una tarea única. También puede crear una continuación que se ejecute después de que alguno o todos los grupos de tareas se completen o se cancelen. Para obtener más información, vea TaskContinueWhenAll() y TaskContinueWhenAny().

Ejemplo

En el método DoSimpleContinuation, se muestra la sintaxis básica de ContinueWith. Observe que la tarea antecedente se proporciona como el parámetro de entrada a la expresión lambda en el método ContinueWith. Esto le permite evaluar el estado de la tarea antecedente antes de realizar cualquier trabajo en la continuación. Utilice esta sobrecarga simple de ContinueWith cuando no tenga que pasar un estado de una tarea a otra.

En el método DoSimpleContinuationWithState, se muestra cómo utilizar ContinueWith para pasar el resultado de la tarea antecedente a la tarea de continuación.

Imports System.IO
Imports System.Threading
Imports System.Threading.Tasks
Module ContinueWith

    Sub Main()
        DoSimpleContinuation()

        Console.WriteLine("Press any key to exit")
        Console.ReadKey()
    End Sub


    Sub DoSimpleContinuation()
        Dim path As String = "C:\users\public\TPLTestFolder\"
        Try
            Dim firstTask = New Task(Sub() CopyDataIntoTempFolder(path))
            Dim secondTask = firstTask.ContinueWith(Sub(t) CreateSummaryFile(path))
            firstTask.Start()
        Catch e As AggregateException
            Console.WriteLine(e.Message)
        End Try
    End Sub

    ' A toy function to simulate a workload
    Sub CopyDataIntoTempFolder(ByVal path__1 As String)
        System.IO.Directory.CreateDirectory(path__1)
        Dim rand As New Random()
        For x As Integer = 0 To 49
            Dim bytes As Byte() = New Byte(999) {}
            rand.NextBytes(bytes)
            Dim filename As String = Path.GetRandomFileName()
            Dim filepath As String = Path.Combine(path__1, filename)
            System.IO.File.WriteAllBytes(filepath, bytes)
        Next
    End Sub

    Sub CreateSummaryFile(ByVal path__1 As String)
        Dim files As String() = System.IO.Directory.GetFiles(path__1)
        Parallel.ForEach(files, Sub(file)
                                    Thread.SpinWait(5000)
                                End Sub)

        System.IO.File.WriteAllText(Path.Combine(path__1, "__SummaryFile.txt"), "did my work")
        Console.WriteLine("Done with task2")
    End Sub

    Sub DoSimpleContinuationWithState()
        Dim nums As Integer() = {19, 17, 21, 4, 13, 8, _
        12, 7, 3, 5}
        Dim f0 = New Task(Of Double)(Function() nums.Average())
        Dim f1 = f0.ContinueWith(Function(t) GetStandardDeviation(nums, t.Result))

        f0.Start()
        Console.WriteLine("the standard deviation is {0}", f1)
    End Sub

    Function GetStandardDeviation(ByVal values As Integer(), ByVal mean As Double) As Double
        Dim d As Double = 0.0R
        For Each n In values
            d += Math.Pow(mean - n, 2)
        Next
        Return Math.Sqrt(d / (values.Length - 1))
    End Function
End Module
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace ContinueWith
{
    class Continuations
    {
        static void Main()
        {
            SimpleContinuation();            

            Console.WriteLine("Press any key to exit");
            Console.ReadKey();
        }

        static void SimpleContinuation()
        {
            string path = @"C:\users\public\TPLTestFolder\";
            try
            {
                var firstTask = new Task(() => CopyDataIntoTempFolder(path));
                var secondTask = firstTask.ContinueWith((t) => CreateSummaryFile(path));
                firstTask.Start();
            }
            catch (AggregateException e)
            {
                Console.WriteLine(e.Message);
            }
        }

        // A toy function to simulate a workload
        static void CopyDataIntoTempFolder(string path)
        {
            System.IO.Directory.CreateDirectory(path);
            Random rand = new Random();
            for (int x = 0; x < 50; x++)
            {
                byte[] bytes = new byte[1000];
                rand.NextBytes(bytes);
                string filename = Path.GetRandomFileName();
                string filepath = Path.Combine(path, filename);
                System.IO.File.WriteAllBytes(filepath, bytes);
            }
        }

        static void CreateSummaryFile(string path)
        {
            string[] files = System.IO.Directory.GetFiles(path);
            Parallel.ForEach(files, (file) =>
                {
                    Thread.SpinWait(5000);
                });

            System.IO.File.WriteAllText(Path.Combine(path, "__SummaryFile.txt"), "did my work");
            Console.WriteLine("Done with task2");
        }

        static void SimpleContinuationWithState()
        {
            int[] nums = { 19, 17, 21, 4, 13, 8, 12, 7, 3, 5 };
            var f0 = new Task<double>(() =>  nums.Average());
            var f1 = f0.ContinueWith( t => GetStandardDeviation(nums, t.Result));

            f0.Start();
            Console.WriteLine("the standard deviation is {0}", f1.Result);          
        }        

        private static double GetStandardDeviation(int[] values, double mean)
        {
            double d = 0.0;
            foreach (var n in values)
            {
                d += Math.Pow(mean - n, 2);
            }
            return Math.Sqrt(d / (values.Length - 1));
        }
    }
}

El parámetro de tipo de Task<TResult> determina el tipo devuelto del delegado. Ese valor devuelto se pasa a la tarea de continuación. Es posible encadenar un número arbitrario de tareas de esta manera.

Vea también

Conceptos

Programación paralela en .NET Framework

Expresiones lambda en PLINQ y TPL

Otros recursos

Tareas de continuación

Paralelismo de tareas (Task Parallel Library)