Programación asincrónica basada en tareasTask-based asynchronous programming

La biblioteca TPL se basa en el concepto de tarea, que representa una operación asincrónica.The Task Parallel Library (TPL) is based on the concept of a task, which represents an asynchronous operation. De cierta forma, una tarea recuerda a un subproceso o elemento de trabajo ThreadPool, pero en un nivel más alto de abstracción.In some ways, a task resembles a thread or ThreadPool work item, but at a higher level of abstraction. El término paralelismo de tareas hace referencia a la ejecución simultánea de una o varias tareas independientes.The term task parallelism refers to one or more independent tasks running concurrently. Las tareas proporcionan dos ventajas fundamentales:Tasks provide two primary benefits:

  • Un uso más eficaz y más escalable de los recursos del sistema.More efficient and more scalable use of system resources.

    En segundo plano, las tareas se ponen en la cola del elemento ThreadPool, que se ha mejorado con algoritmos que determinan y ajustan el número de subprocesos y que ofrecen el equilibrio de carga para maximizar el rendimiento.Behind the scenes, tasks are queued to the ThreadPool, which has been enhanced with algorithms that determine and adjust to the number of threads and that provide load balancing to maximize throughput. Esto hace que las tareas resulten relativamente ligeras y que, por tanto, pueda crearse un gran número de ellas para habilitar un paralelismo pormenorizado.This makes tasks relatively lightweight, and you can create many of them to enable fine-grained parallelism.

  • Un mayor control mediante programación del que se puede conseguir con un subproceso o un elemento de trabajo.More programmatic control than is possible with a thread or work item.

    Las tareas y el marco que se crea en torno a ellas proporcionan un amplio conjunto de API que admiten el uso de esperas, cancelaciones, continuaciones, control robusto de excepciones, estado detallado, programación personalizada, y más.Tasks and the framework built around them provide a rich set of APIs that support waiting, cancellation, continuations, robust exception handling, detailed status, custom scheduling, and more.

Por estos dos motivos, en .NET Framework, TPL es la API preferida para escribir código multiproceso, asincrónico y paralelo.For both of these reasons, in the .NET Framework, TPL is the preferred API for writing multi-threaded, asynchronous, and parallel code.

Crear y ejecutar tareas implícitamenteCreating and running tasks implicitly

El método Parallel.Invoke proporciona una manera conveniente de ejecutar cualquier número de instrucciones arbitrarias simultáneamente.The Parallel.Invoke method provides a convenient way to run any number of arbitrary statements concurrently. Pase un delegado Action por cada elemento de trabajo.Just pass in an Action delegate for each item of work. La manera más fácil de crear estos delegados es con expresiones lambda.The easiest way to create these delegates is to use lambda expressions. La expresión lambda puede llamar a un método con nombre o proporcionar el código alineado.The lambda expression can either call a named method or provide the code inline. En el siguiente ejemplo se muestra una llamada a Invoke básica que crea e inicia dos tareas que se ejecutan a la vez.The following example shows a basic Invoke call that creates and starts two tasks that run concurrently. La primera tarea se representa mediante una expresión lambda que llama a un método denominado DoSomeWork y la segunda tarea se representa mediante una expresión lambda que llama a un método denominado DoSomeOtherWork.The first task is represented by a lambda expression that calls a method named DoSomeWork, and the second task is represented by a lambda expression that calls a method named DoSomeOtherWork.

Nota

En esta documentación, se utilizan expresiones lambda para definir delegados en la TPL.This documentation uses lambda expressions to define delegates in TPL. Si no está familiarizado con las expresiones lambda de C# o Visual Basic, consulte Lambda Expressions in PLINQ and TPL (Expresiones lambda en PLINQ y TPL).If you are not familiar with lambda expressions in C# or Visual Basic, see Lambda Expressions in PLINQ and TPL.

Parallel.Invoke(() => DoSomeWork(), () => DoSomeOtherWork());
Parallel.Invoke(Sub() DoSomeWork(), Sub() DoSomeOtherWork())

Nota

El número de instancias de Task que Invoke crea en segundo plano no es necesariamente igual al número de delegados que se proporcionan.The number of Task instances that are created behind the scenes by Invoke is not necessarily equal to the number of delegates that are provided. La TPL puede emplear varias optimizaciones, sobre todo con grandes números de delegados.The TPL may employ various optimizations, especially with large numbers of delegates.

Para obtener más información, vea Cómo: Usar Parallel.Invoke para ejecutar operaciones en paralelo.For more information, see How to: Use Parallel.Invoke to Execute Parallel Operations.

Para tener un mayor control de la ejecución de tareas o para devolver un valor de la tarea, debe trabajar con objetos Task más explícitamente.For greater control over task execution or to return a value from the task, you have to work with Task objects more explicitly.

Crear y ejecutar tareas explícitamenteCreating and running tasks explicitly

Una tarea que no devuelve un valor se representa mediante la clase System.Threading.Tasks.Task .A task that does not return a value is represented by the System.Threading.Tasks.Task class. Una tarea que devuelve un valor se representa mediante la clase System.Threading.Tasks.Task<TResult>, que se hereda de Task.A task that returns a value is represented by the System.Threading.Tasks.Task<TResult> class, which inherits from Task. El objeto de tarea controla los detalles de la infraestructura y proporciona métodos y propiedades a los que se puede obtener acceso desde el subproceso que realiza la llamada a lo largo de la duración de la tarea.The task object handles the infrastructure details and provides methods and properties that are accessible from the calling thread throughout the lifetime of the task. Por ejemplo, se puede tener acceso a la propiedad Status de una tarea en cualquier momento para determinar si ha empezado a ejecutarse, si se ha ejecutado hasta su finalización, si se ha cancelado o si se ha producido una excepción.For example, you can access the Status property of a task at any time to determine whether it has started running, ran to completion, was canceled, or has thrown an exception. El estado se representa mediante la enumeración TaskStatus.The status is represented by a TaskStatus enumeration.

Cuando se crea una tarea, se proporciona un delegado de usuario que encapsula el código que la tarea va a ejecutar.When you create a task, you give it a user delegate that encapsulates the code that the task will execute. El delegado se puede expresar como un delegado con nombre, un método anónimo o una expresión lambda.The delegate can be expressed as a named delegate, an anonymous method, or a lambda expression. Las expresiones lambda pueden contener una llamada a un método con nombre, tal y como se muestra en el siguiente ejemplo.Lambda expressions can contain a call to a named method, as shown in the following example. Observe que el ejemplo incluye una llamada al método Task.Wait para garantizar que la ejecución de la tarea se complete antes de que finalice la aplicación de modo de consola.Note that the example includes a call to the Task.Wait method to ensure that the task completes execution before the console mode application ends.

using System;
using System.Threading;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      Thread.CurrentThread.Name = "Main";

      // Create a task and supply a user delegate by using a lambda expression. 
      Task taskA = new Task( () => Console.WriteLine("Hello from taskA."));
      // Start the task.
      taskA.Start();

      // Output a message from the calling thread.
      Console.WriteLine("Hello from thread '{0}'.", 
                        Thread.CurrentThread.Name);
      taskA.Wait();
   }
}
// The example displays output like the following:
//       Hello from thread 'Main'.
//       Hello from taskA.
Imports System.Threading
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
        Thread.CurrentThread.Name = "Main"
        
        ' Create a task and supply a user delegate by using a lambda expression. 
        Dim taskA = New Task(Sub() Console.WriteLine("Hello from taskA."))
        ' Start the task.
        taskA.Start()

        ' Output a message from the calling thread.
        Console.WriteLine("Hello from thread '{0}'.", 
                          Thread.CurrentThread.Name)
        taskA.Wait()
   End Sub
End Module
' The example displays output like the following:
'    Hello from thread 'Main'.
'    Hello from taskA.

También se pueden usar los métodos Task.Run para crear e iniciar una tarea en una sola operación.You can also use the Task.Run methods to create and start a task in one operation. Para administrar la tarea, los métodos Run usan el programador de tareas predeterminado, independientemente qué programador de tareas esté asociado al subproceso actual.To manage the task, the Run methods use the default task scheduler, regardless of which task scheduler is associated with the current thread. Los métodos Run son el modo preferido para crear e iniciar tareas cuando no se necesita más control sobre la creación y la programación de la tarea.The Run methods are the preferred way to create and start tasks when more control over the creation and scheduling of the task is not needed.

using System;
using System.Threading;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      Thread.CurrentThread.Name = "Main";

      // Define and run the task.
      Task taskA = Task.Run( () => Console.WriteLine("Hello from taskA."));

      // Output a message from the calling thread.
      Console.WriteLine("Hello from thread '{0}'.", 
                          Thread.CurrentThread.Name);
      taskA.Wait();
   }
}
// The example displays output like the following:
//       Hello from thread 'Main'.
//       Hello from taskA.
Imports System.Threading
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
        Thread.CurrentThread.Name = "Main"
        
        Dim taskA As Task = Task.Run(Sub() Console.WriteLine("Hello from taskA."))

        ' Output a message from the calling thread.
        Console.WriteLine("Hello from thread '{0}'.", 
                          Thread.CurrentThread.Name)
        taskA.Wait()
   End Sub
End Module
' The example displays output like the following:
'    Hello from thread 'Main'.
'    Hello from taskA.

También se puede usar el método TaskFactory.StartNew para crear e iniciar una tarea en una sola operación.You can also use the TaskFactory.StartNew method to create and start a task in one operation. Utilice este método cuando la creación y la programación no tengan que ser independientes y necesite opciones de creación de tareas adicionales o el uso de un programador concreto, o cuando necesita pasar información de estado adicional en la tarea que se puede recuperar mediante su propiedad Task.AsyncState, como se muestra en el ejemplo siguiente.Use this method when creation and scheduling do not have to be separated and you require additional task creation options or the use of a specific scheduler, or when you need to pass additional state into the task that you can retrieve through its Task.AsyncState property, as shown in the following example.

using System;
using System.Threading;
using System.Threading.Tasks;

class CustomData
{
   public long CreationTime;
   public int Name; 
   public int ThreadNum;
}

public class Example
{
   public static void Main()
   {
      Task[] taskArray = new Task[10];
      for (int i = 0; i < taskArray.Length; i++) {
         taskArray[i] = Task.Factory.StartNew( (Object obj ) => {
                                                  CustomData data = obj as CustomData;
                                                  if (data == null) 
                                                     return;
                                     
                                                  data.ThreadNum = Thread.CurrentThread.ManagedThreadId;
                                               },
                                               new CustomData() {Name = i, CreationTime = DateTime.Now.Ticks} );
      }
      Task.WaitAll(taskArray);     
      foreach (var task in taskArray) {
         var data = task.AsyncState as CustomData;
         if (data != null)
            Console.WriteLine("Task #{0} created at {1}, ran on thread #{2}.",
                              data.Name, data.CreationTime, data.ThreadNum);
      }                     
   }
}
// The example displays output like the following:
//       Task #0 created at 635116412924597583 on thread #3.
//       Task #1 created at 635116412924607584 on thread #4.
//       Task #3 created at 635116412924607584 on thread #4.
//       Task #4 created at 635116412924607584 on thread #4.
//       Task #2 created at 635116412924607584 on thread #3.
//       Task #6 created at 635116412924607584 on thread #3.
//       Task #5 created at 635116412924607584 on thread #4.
//       Task #8 created at 635116412924607584 on thread #4.
//       Task #7 created at 635116412924607584 on thread #3.
//       Task #9 created at 635116412924607584 on thread #4.
Imports System.Threading
Imports System.Threading.Tasks

Class CustomData
   Public CreationTime As Long
   Public Name As Integer
   Public ThreadNum As Integer
End Class

Module Example
   Public Sub Main()
      Dim taskArray(9) As Task
      For i As Integer = 0 To taskArray.Length - 1
         taskArray(i) = Task.Factory.StartNew(Sub(obj As Object)
                                                 Dim data As CustomData = TryCast(obj, CustomData)
                                                 If data Is Nothing Then Return
                                     
                                                 data.ThreadNum = Thread.CurrentThread.ManagedThreadId
                                              End Sub,
                                              New CustomData With {.Name = i, .CreationTime = DateTime.Now.Ticks} )
      Next
      Task.WaitAll(taskArray)     

      For Each task In taskArray
         Dim data = TryCast(task.AsyncState, CustomData)
         If data IsNot Nothing Then
            Console.WriteLine("Task #{0} created at {1}, ran on thread #{2}.",
                              data.Name, data.CreationTime, data.ThreadNum)
         End If
      Next
   End Sub
End Module
' The example displays output like the following:
'    Task #0 created at 635116451245250515, ran on thread #3, RanToCompletion
'    Task #1 created at 635116451245270515, ran on thread #4, RanToCompletion
'    Task #2 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #3 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #4 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #5 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #6 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #7 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #8 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #9 created at 635116451245270515, ran on thread #3, RanToCompletion

Task y Task<TResult> exponen en cada caso una propiedad Factory estática que devuelve una instancia predeterminada de TaskFactory, por lo que se puede llamar al método como Task.Factory.StartNew().Task and Task<TResult> each expose a static Factory property that returns a default instance of TaskFactory, so that you can call the method as Task.Factory.StartNew(). Asimismo, en el siguiente ejemplo, dado que las tareas son de tipo System.Threading.Tasks.Task<TResult>, cada una tiene una propiedad Task<TResult>.Result pública que contiene el resultado del cálculo.Also, in the following example, because the tasks are of type System.Threading.Tasks.Task<TResult>, they each have a public Task<TResult>.Result property that contains the result of the computation. Las tareas se ejecutan de forma asincrónica y pueden completarse en cualquier orden.The tasks run asynchronously and may complete in any order. Si se obtiene acceso a la propiedad Result antes de que el cálculo se complete, la propiedad bloqueará el subproceso hasta que el valor esté disponible.If the Result property is accessed before the computation finishes, the property blocks the calling thread until the value is available.

using System;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
        Task<Double>[] taskArray = { Task<Double>.Factory.StartNew(() => DoComputation(1.0)),
                                     Task<Double>.Factory.StartNew(() => DoComputation(100.0)), 
                                     Task<Double>.Factory.StartNew(() => DoComputation(1000.0)) };

        var results = new Double[taskArray.Length];
        Double sum = 0;
        
        for (int i = 0; i < taskArray.Length; i++) {
            results[i] = taskArray[i].Result;
            Console.Write("{0:N1} {1}", results[i], 
                              i == taskArray.Length - 1 ? "= " : "+ ");
            sum += results[i];
        }
        Console.WriteLine("{0:N1}", sum);
   }

   private static Double DoComputation(Double start)
   {
      Double sum = 0;
      for (var value = start; value <= start + 10; value += .1)
         sum += value;

      return sum; 
   }
}
// The example displays the following output:
//        606.0 + 10,605.0 + 100,495.0 = 111,706.0
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
        Dim taskArray() = { Task(Of Double).Factory.StartNew(Function() DoComputation(1.0)),
                            Task(Of Double).Factory.StartNew(Function() DoComputation(100.0)), 
                            Task(Of Double).Factory.StartNew(Function() DoComputation(1000.0)) }

        Dim results(taskArray.Length - 1) As Double
        Dim sum As Double
        
        For i As Integer = 0 To taskArray.Length - 1
            results(i) = taskArray(i).Result
            Console.Write("{0:N1} {1}", results(i), 
                              If(i = taskArray.Length - 1, "= ", "+ "))
            sum += results(i)
        Next
        Console.WriteLine("{0:N1}", sum)
   End Sub
   
   Private Function DoComputation(start As Double) As Double
      Dim sum As Double
      For value As Double = start To start + 10 Step .1
         sum += value
      Next
      Return sum 
   End Function
End Module
' The example displays the following output:
'       606.0 + 10,605.0 + 100,495.0 = 111,706.0

Para obtener más información, vea Cómo: Devolver un valor a partir de una tarea.For more information, see How to: Return a Value from a Task.

Cuando se usa una expresión lambda para crear un delegado, se obtiene acceso a todas las variables que están visibles en ese momento en el código fuente.When you use a lambda expression to create a delegate, you have access to all the variables that are visible at that point in your source code. Sin embargo, en algunos casos, sobre todo en los bucles, una expresión lambda no captura la variable como cabría esperar.However, in some cases, most notably within loops, a lambda doesn't capture the variable as expected. Captura solo el valor final, no el valor tal y como se transforma después de cada iteración.It only captures the final value, not the value as it mutates after each iteration. En el siguiente ejemplo se ilustra el problema.The following example illustrates the problem. Pasa un contador de bucle a una expresión lambda que crea instancias de un objeto CustomData y usa el contador de bucle como identificador del objeto.It passes a loop counter to a lambda expression that instantiates a CustomData object and uses the loop counter as the object's identifier. Como muestra la salida del ejemplo, cada objeto CustomData tiene un identificador idéntico.As the output from the example shows, each CustomData object has an identical identifier.

using System;
using System.Threading;
using System.Threading.Tasks;

class CustomData
{
   public long CreationTime;
   public int Name; 
   public int ThreadNum;
}

public class Example
{
   public static void Main()
   {
      // Create the task object by using an Action(Of Object) to pass in the loop
      // counter. This produces an unexpected result.
      Task[] taskArray = new Task[10];
      for (int i = 0; i < taskArray.Length; i++) {
         taskArray[i] = Task.Factory.StartNew( (Object obj) => {
                                                 var data = new CustomData() {Name = i, CreationTime = DateTime.Now.Ticks}; 
                                                 data.ThreadNum = Thread.CurrentThread.ManagedThreadId;
                                                 Console.WriteLine("Task #{0} created at {1} on thread #{2}.",
                                                                   data.Name, data.CreationTime, data.ThreadNum);
                                               },
                                              i );
      }
      Task.WaitAll(taskArray);     
   }
}
// The example displays output like the following:
//       Task #10 created at 635116418427727841 on thread #4.
//       Task #10 created at 635116418427737842 on thread #4.
//       Task #10 created at 635116418427737842 on thread #4.
//       Task #10 created at 635116418427737842 on thread #4.
//       Task #10 created at 635116418427737842 on thread #4.
//       Task #10 created at 635116418427737842 on thread #4.
//       Task #10 created at 635116418427727841 on thread #3.
//       Task #10 created at 635116418427747843 on thread #3.
//       Task #10 created at 635116418427747843 on thread #3.
//       Task #10 created at 635116418427737842 on thread #4.
Imports System.Threading
Imports System.Threading.Tasks

Class CustomData
   Public CreationTime As Long
   Public Name As Integer
   Public ThreadNum As Integer
End Class

Module Example
   Public Sub Main()
      ' Create the task object by using an Action(Of Object) to pass in the loop
      ' counter. This produces an unexpected result.
      Dim taskArray(9) As Task
      For i As Integer = 0 To taskArray.Length - 1
         taskArray(i) = Task.Factory.StartNew(Sub(obj As Object)
                                                 Dim data As New CustomData With {.Name = i, .CreationTime = DateTime.Now.Ticks} 
                                                 data.ThreadNum = Thread.CurrentThread.ManagedThreadId
                                                 Console.WriteLine("Task #{0} created at {1} on thread #{2}.",
                                                                   data.Name, data.CreationTime, data.ThreadNum)
                                              End Sub,
                                              i )
      Next
      Task.WaitAll(taskArray)     
   End Sub
End Module
' The example displays output like the following:
'       Task #10 created at 635116418427727841 on thread #4.
'       Task #10 created at 635116418427737842 on thread #4.
'       Task #10 created at 635116418427737842 on thread #4.
'       Task #10 created at 635116418427737842 on thread #4.
'       Task #10 created at 635116418427737842 on thread #4.
'       Task #10 created at 635116418427737842 on thread #4.
'       Task #10 created at 635116418427727841 on thread #3.
'       Task #10 created at 635116418427747843 on thread #3.
'       Task #10 created at 635116418427747843 on thread #3.
'       Task #10 created at 635116418427737842 on thread #4.

Puede obtener acceso al valor en cada iteración si proporciona un objeto de estado a una tarea a través de su constructor.You can access the value on each iteration by providing a state object to a task through its constructor. En el ejemplo siguiente se modifica el ejemplo anterior utilizando el contador de bucle al crear el objeto CustomData que, a su vez, se pasa a la expresión lambda.The following example modifies the previous example by using the loop counter when creating the CustomData object, which, in turn, is passed to the lambda expression. Como muestra el resultado del ejemplo, cada objeto CustomData tiene ahora un identificador único basado en el valor del contador de bucle cuando se creó la instancia del objeto.As the output from the example shows, each CustomData object now has a unique identifier based on the value of the loop counter at the time the object was instantiated.

using System;
using System.Threading;
using System.Threading.Tasks;

class CustomData
{
   public long CreationTime;
   public int Name; 
   public int ThreadNum;
}

public class Example
{
   public static void Main()
   {
      // Create the task object by using an Action(Of Object) to pass in custom data
      // to the Task constructor. This is useful when you need to capture outer variables
      // from within a loop. 
      Task[] taskArray = new Task[10];
      for (int i = 0; i < taskArray.Length; i++) {
         taskArray[i] = Task.Factory.StartNew( (Object obj ) => {
                                                  CustomData data = obj as CustomData;
                                                  if (data == null) 
                                                     return;
                                     
                                                  data.ThreadNum = Thread.CurrentThread.ManagedThreadId;
                                                  Console.WriteLine("Task #{0} created at {1} on thread #{2}.",
                                                                   data.Name, data.CreationTime, data.ThreadNum);
                                               },
                                               new CustomData() {Name = i, CreationTime = DateTime.Now.Ticks} );
      }
      Task.WaitAll(taskArray);     
   }
}
// The example displays output like the following:
//       Task #0 created at 635116412924597583 on thread #3.
//       Task #1 created at 635116412924607584 on thread #4.
//       Task #3 created at 635116412924607584 on thread #4.
//       Task #4 created at 635116412924607584 on thread #4.
//       Task #2 created at 635116412924607584 on thread #3.
//       Task #6 created at 635116412924607584 on thread #3.
//       Task #5 created at 635116412924607584 on thread #4.
//       Task #8 created at 635116412924607584 on thread #4.
//       Task #7 created at 635116412924607584 on thread #3.
//       Task #9 created at 635116412924607584 on thread #4.
Imports System.Threading
Imports System.Threading.Tasks

Class CustomData
   Public CreationTime As Long
   Public Name As Integer
   Public ThreadNum As Integer
End Class

Module Example
   Public Sub Main()
      ' Create the task object by using an Action(Of Object) to pass in custom data
      ' to the Task constructor. This is useful when you need to capture outer variables
      ' from within a loop. 
      Dim taskArray(9) As Task
      For i As Integer = 0 To taskArray.Length - 1
         taskArray(i) = Task.Factory.StartNew(Sub(obj As Object)
                                                 Dim data As CustomData = TryCast(obj, CustomData)
                                                 If data Is Nothing Then Return
                                     
                                                 data.ThreadNum = Thread.CurrentThread.ManagedThreadId
                                                 Console.WriteLine("Task #{0} created at {1} on thread #{2}.",
                                                                   data.Name, data.CreationTime, data.ThreadNum)
                                              End Sub,
                                              New CustomData With {.Name = i, .CreationTime = DateTime.Now.Ticks} )
      Next
      Task.WaitAll(taskArray)     
   End Sub
End Module
' The example displays output like the following:
'       Task #0 created at 635116412924597583 on thread #3.
'       Task #1 created at 635116412924607584 on thread #4.
'       Task #3 created at 635116412924607584 on thread #4.
'       Task #4 created at 635116412924607584 on thread #4.
'       Task #2 created at 635116412924607584 on thread #3.
'       Task #6 created at 635116412924607584 on thread #3.
'       Task #5 created at 635116412924607584 on thread #4.
'       Task #8 created at 635116412924607584 on thread #4.
'       Task #7 created at 635116412924607584 on thread #3.
'       Task #9 created at 635116412924607584 on thread #4.

Este estado se pasa como argumento al delegado de la tarea y permite tener acceso desde el objeto de tarea mediante la propiedad Task.AsyncState.This state is passed as an argument to the task delegate, and it can be accessed from the task object by using the Task.AsyncState property. El ejemplo siguiente es una variación del ejemplo anterior.The following example is a variation on the previous example. Utiliza la propiedad AsyncState para mostrar información sobre los objetos CustomData pasados a la expresión lambda.It uses the AsyncState property to display information about the CustomData objects passed to the lambda expression.

using System;
using System.Threading;
using System.Threading.Tasks;

class CustomData
{
   public long CreationTime;
   public int Name; 
   public int ThreadNum;
}

public class Example
{
   public static void Main()
   {
      Task[] taskArray = new Task[10];
      for (int i = 0; i < taskArray.Length; i++) {
         taskArray[i] = Task.Factory.StartNew( (Object obj ) => {
                                                  CustomData data = obj as CustomData;
                                                  if (data == null) 
                                                     return;
                                     
                                                  data.ThreadNum = Thread.CurrentThread.ManagedThreadId;
                                               },
                                               new CustomData() {Name = i, CreationTime = DateTime.Now.Ticks} );
      }
      Task.WaitAll(taskArray);     
      foreach (var task in taskArray) {
         var data = task.AsyncState as CustomData;
         if (data != null)
            Console.WriteLine("Task #{0} created at {1}, ran on thread #{2}.",
                              data.Name, data.CreationTime, data.ThreadNum);
      }                     
   }
}
// The example displays output like the following:
//       Task #0 created at 635116412924597583 on thread #3.
//       Task #1 created at 635116412924607584 on thread #4.
//       Task #3 created at 635116412924607584 on thread #4.
//       Task #4 created at 635116412924607584 on thread #4.
//       Task #2 created at 635116412924607584 on thread #3.
//       Task #6 created at 635116412924607584 on thread #3.
//       Task #5 created at 635116412924607584 on thread #4.
//       Task #8 created at 635116412924607584 on thread #4.
//       Task #7 created at 635116412924607584 on thread #3.
//       Task #9 created at 635116412924607584 on thread #4.
Imports System.Threading
Imports System.Threading.Tasks

Class CustomData
   Public CreationTime As Long
   Public Name As Integer
   Public ThreadNum As Integer
End Class

Module Example
   Public Sub Main()
      Dim taskArray(9) As Task
      For i As Integer = 0 To taskArray.Length - 1
         taskArray(i) = Task.Factory.StartNew(Sub(obj As Object)
                                                 Dim data As CustomData = TryCast(obj, CustomData)
                                                 If data Is Nothing Then Return
                                     
                                                 data.ThreadNum = Thread.CurrentThread.ManagedThreadId
                                              End Sub,
                                              New CustomData With {.Name = i, .CreationTime = DateTime.Now.Ticks} )
      Next
      Task.WaitAll(taskArray)     

      For Each task In taskArray
         Dim data = TryCast(task.AsyncState, CustomData)
         If data IsNot Nothing Then
            Console.WriteLine("Task #{0} created at {1}, ran on thread #{2}.",
                              data.Name, data.CreationTime, data.ThreadNum)
         End If
      Next
   End Sub
End Module
' The example displays output like the following:
'    Task #0 created at 635116451245250515, ran on thread #3, RanToCompletion
'    Task #1 created at 635116451245270515, ran on thread #4, RanToCompletion
'    Task #2 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #3 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #4 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #5 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #6 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #7 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #8 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #9 created at 635116451245270515, ran on thread #3, RanToCompletion

Identificador de tareaTask ID

Cada tarea recibe un identificador entero que la identifica de manera inequívoca en un dominio de aplicación y al que se puede obtener acceso mediante la propiedad Task.Id.Every task receives an integer ID that uniquely identifies it in an application domain and can be accessed by using the Task.Id property. El identificador resulta útil para ver información sobre la tarea en las ventanas Pilas paralelas y Tareas del depurador de Visual Studio.The ID is useful for viewing task information in the Visual Studio debugger Parallel Stacks and Tasks windows. El identificador se crea de forma diferida, lo que significa que no se crea hasta que se solicita; por tanto, una tarea podrá tener un identificador diferente cada vez que se ejecute el programa.The ID is lazily created, which means that it isn't created until it is requested; therefore, a task may have a different ID every time the program is run. Para más información sobre cómo ver los identificadores de tarea en el depurador, consulte Usar la ventana Tareas y Usar la ventana Pilas paralelas.For more information about how to view task IDs in the debugger, see Using the Tasks Window and Using the Parallel Stacks Window.

Opciones de creación de tareasTask creation options

La mayoría de las API que crean tareas proporcionan sobrecargas que aceptan un parámetro TaskCreationOptions.Most APIs that create tasks provide overloads that accept a TaskCreationOptions parameter. Al especificar una de estas opciones, se le está indicando al programador cómo se programa la tarea en el grupo de subprocesos.By specifying one of these options, you tell the task scheduler how to schedule the task on the thread pool. En la tabla siguiente se muestran las diversas opciones de creación de tareas.The following table lists the various task creation options.

TaskCreationOptions valor de parámetroTaskCreationOptions parameter value DESCRIPCIÓNDescription
None Es la opción predeterminada si no se especifica ninguna opción.The default when no option is specified. El programador usa su heurística predeterminada para programar la tarea.The scheduler uses its default heuristics to schedule the task.
PreferFairness Especifica que la tarea debe programarse de modo que las tareas creadas anteriormente tengan más posibilidades de ejecutarse antes y que las tareas posteriormente tengan más posibilidades de ejecutarse después.Specifies that the task should be scheduled so that tasks created sooner will be more likely to be executed sooner, and tasks created later will be more likely to execute later.
LongRunning Especifica que la tarea representa una operación de ejecución prolongada.Specifies that the task represents a long-running operation.
AttachedToParent Especifica que una tarea debe crearse como un elemento secundario asociado de la tarea actual, si existe.Specifies that a task should be created as an attached child of the current task, if one exists. Para más información, consulte Attached and Detached Child Tasks (Tareas secundarias asociadas y desasociadas).For more information, see Attached and Detached Child Tasks.
DenyChildAttach Especifica que, si una tarea interna especifica la opción AttachedToParent, esa tarea no se convertirá en una tarea secundaria asociada.Specifies that if an inner task specifies the AttachedToParent option, that task will not become an attached child task.
HideScheduler Especifica que el programador de tareas para tareas creadas llamando a métodos como TaskFactory.StartNew o Task<TResult>.ContinueWith desde dentro de una tarea determinada es el programador predeterminado en lugar del programador en el que se ejecuta esta tarea.Specifies that the task scheduler for tasks created by calling methods like TaskFactory.StartNew or Task<TResult>.ContinueWith from within a particular task is the default scheduler instead of the scheduler on which this task is running.

Las opciones pueden combinarse con una operación OR.The options may be combined by using a bitwise OR operation. En el ejemplo siguiente se muestra una tarea que tiene las opciones LongRunning y PreferFairness.The following example shows a task that has the LongRunning and PreferFairness option.

var task3 = new Task(() => MyLongRunningMethod(),
                    TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness);
task3.Start();

Dim task3 = New Task(Sub() MyLongRunningMethod(),
                        TaskCreationOptions.LongRunning Or TaskCreationOptions.PreferFairness)
task3.Start()

Tareas, subprocesos y referencia culturalTasks, threads, and culture

Cada subproceso tiene asociada una referencia cultural asociada y una interfaz de usuario de referencia cultural, que está definida por las propiedades Thread.CurrentCulture y Thread.CurrentUICulture, respectivamente.Each thread has an associated culture and UI culture, which is defined by the Thread.CurrentCulture and Thread.CurrentUICulture properties, respectively. La referencia cultural de un subproceso se usa en operaciones como dar formato, analizar, ordenar y comparar cadenas.A thread's culture is used in such operations as formatting, parsing, sorting, and string comparison. La referencia cultural de la interfaz de usuario de un subproceso se usa en la búsqueda de recursos.A thread's UI culture is used in resource lookup. Por lo general, a menos que especifique una referencia cultural predeterminada para todos los subprocesos de un dominio de aplicación usando las propiedades CultureInfo.DefaultThreadCurrentCulture y CultureInfo.DefaultThreadCurrentUICulture, la referencia cultural del sistema define la referencia cultural predeterminada y la referencia cultural de la interfaz de usuario de un subproceso.Ordinarily, unless you specify a default culture for all the threads in an application domain by using the CultureInfo.DefaultThreadCurrentCulture and CultureInfo.DefaultThreadCurrentUICulture properties, the default culture and UI culture of a thread is defined by the system culture. Si establece explícitamente la referencia cultural de un subproceso e inicia un nuevo subproceso, el nuevo subproceso no hereda la referencia cultural del subproceso que realiza la llamada; en su lugar, su referencia cultural es la referencia cultural predeterminada del sistema.If you explicitly set a thread's culture and launch a new thread, the new thread does not inherit the culture of the calling thread; instead, its culture is the default system culture. El modelo de programación basado en tareas para las aplicaciones destinadas a versiones de .NET Framework anteriores a .NET Framework 4.6 siguen este procedimiento.The task-based programming model for apps that target versions of the .NET Framework prior to .NET Framework 4.6 adhere to this practice.

Importante

Tenga en cuenta que la referencia cultural del subproceso que realiza la llamada como parte del contexto de una tarea se aplica a las aplicaciones destinadas a .NET Framework 4.6, no a las que se ejecutan en .NET Framework 4.6.Note that the calling thread's culture as part of a task's context applies to apps that target the .NET Framework 4.6, not apps that run under the .NET Framework 4.6. Puede elegir como destino una versión determinada de .NET Framework al crear el proyecto en Visual Studio seleccionado esta versión en la lista desplegable situada en la parte superior del cuadro de diálogo Nuevo proyecto o fuera de Visual Studio puede usar el atributo TargetFrameworkAttribute.You can target a particular version of the .NET Framework when you create your project in Visual Studio by selecting that version from the dropdown list at the top of the New Project dialog box, or outside of Visual Studio you can use the TargetFrameworkAttribute attribute. Para las aplicaciones destinadas a versiones de .NET Framework anteriores a .NET Framework 4.6 o que no están destinadas a una versión específica de .NET Framework, la referencia cultural del subproceso en el que se ejecuta sigue determinando la referencia cultural de la tarea.For apps that target versions of the .NET Framework prior to the .NET Framework 4.6, or that do not target a specific version of the .NET Framework, a task's culture continues to be determined by the culture of the thread on which it runs.

A partir de las aplicaciones destinadas a .NET Framework 4.6, cada tarea hereda la referencia cultural del subproceso que realiza la llamada, aunque la tarea se ejecute de forma asincrónica en un subproceso del grupo de subprocesos.Starting with apps that target the .NET Framework 4.6, the calling thread's culture is inherited by each task, even if the task runs asynchronously on a thread pool thread.

Esto se muestra en el ejemplo siguiente.The following example provides a simple illustration. Usa el atributo TargetFrameworkAttribute para elegir .NET Framework 4.6 como destino y cambia la referencia cultural actual de la aplicación a francés (Francia) o, si francés (Francia) es la referencia cultural actual, a inglés (Estados Unidos).It uses the TargetFrameworkAttribute attribute to target the .NET Framework 4.6 and changes the app's current culture to either French (France) or, if French (France) is already the current culture, English (United States). Después, invoca un delegado llamado formatDelegate que devuelve algunos números con el formato de valores de moneda de la nueva referencia cultural.It then invokes a delegate named formatDelegate that returns some numbers formatted as currency values in the new culture. Tenga en cuenta si el delegado se ejecuta como una tarea tanto de forma sincrónica como asincrónica, devuelve el resultado esperado porque la tarea asincrónica hereda la referencia cultural del subproceso que realiza la llamada.Note that whether the delegate as a task either synchronously or asynchronously, it returns the expected result because the culture of the calling thread is inherited by the asynchronous task.

using System;
using System.Globalization;
using System.Runtime.Versioning;
using System.Threading;
using System.Threading.Tasks;

[assembly:TargetFramework(".NETFramework,Version=v4.6")]

public class Example
{
   
   public static void Main()
   {
       decimal[] values = { 163025412.32m, 18905365.59m };
       string formatString = "C2";
       Func<String> formatDelegate = () => { string output = String.Format("Formatting using the {0} culture on thread {1}.\n",
                                                                           CultureInfo.CurrentCulture.Name,
                                                                           Thread.CurrentThread.ManagedThreadId);
                                             foreach (var value in values)
                                                output += String.Format("{0}   ", value.ToString(formatString));
                                                   
                                             output += Environment.NewLine;
                                             return output;
                                           };
       
       Console.WriteLine("The example is running on thread {0}", 
                         Thread.CurrentThread.ManagedThreadId);
       // Make the current culture different from the system culture.
       Console.WriteLine("The current culture is {0}", 
                         CultureInfo.CurrentCulture.Name);
       if (CultureInfo.CurrentCulture.Name == "fr-FR")
          Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
       else
          Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR");

       Console.WriteLine("Changed the current culture to {0}.\n",
                         CultureInfo.CurrentCulture.Name);
       
       // Execute the delegate synchronously.
       Console.WriteLine("Executing the delegate synchronously:");
       Console.WriteLine(formatDelegate());
       
       // Call an async delegate to format the values using one format string.
       Console.WriteLine("Executing a task asynchronously:"); 
       var t1 = Task.Run(formatDelegate);
       Console.WriteLine(t1.Result);
       
       Console.WriteLine("Executing a task synchronously:");
       var t2 = new Task<String>(formatDelegate); 
       t2.RunSynchronously();
       Console.WriteLine(t2.Result);
   }
}
// The example displays the following output:
//         The example is running on thread 1
//         The current culture is en-US
//         Changed the current culture to fr-FR.
//
//         Executing the delegate synchronously:
//         Formatting using the fr-FR culture on thread 1.
//         163 025 412,32 €   18 905 365,59 €
//
//         Executing a task asynchronously:
//         Formatting using the fr-FR culture on thread 3.
//         163 025 412,32 €   18 905 365,59 €
//
//         Executing a task synchronously:
//         Formatting using the fr-FR culture on thread 1.
//         163 025 412,32 €   18 905 365,59 €
// If the TargetFrameworkAttribute statement is removed, the example
// displays the following output:
//          The example is running on thread 1
//          The current culture is en-US
//          Changed the current culture to fr-FR.
//
//          Executing the delegate synchronously:
//          Formatting using the fr-FR culture on thread 1.
//          163 025 412,32 €   18 905 365,59 €
//
//          Executing a task asynchronously:
//          Formatting using the en-US culture on thread 3.
//          $163,025,412.32   $18,905,365.59
//
//          Executing a task synchronously:
//          Formatting using the fr-FR culture on thread 1.
//          163 025 412,32 €   18 905 365,59 €
Imports System.Globalization
Imports System.Runtime.Versioning
Imports System.Threading
Imports System.Threading.Tasks

<Assembly:TargetFramework(".NETFramework,Version=v4.6")>

Module Example
   Public Sub Main()
       Dim values() As Decimal = { 163025412.32d, 18905365.59d }
       Dim formatString As String = "C2"
       Dim formatDelegate As Func(Of String) = Function()
                                                  Dim output As String = String.Format("Formatting using the {0} culture on thread {1}.",
                                                                                       CultureInfo.CurrentCulture.Name,
                                                                                       Thread.CurrentThread.ManagedThreadId)
                                                  output += Environment.NewLine
                                                  For Each value In values
                                                     output += String.Format("{0}   ", value.ToString(formatString))
                                                  Next 
                                                  output += Environment.NewLine
                                                  Return output
                                               End Function
       
       Console.WriteLine("The example is running on thread {0}", 
                         Thread.CurrentThread.ManagedThreadId)
       ' Make the current culture different from the system culture.
       Console.WriteLine("The current culture is {0}", 
                         CultureInfo.CurrentCulture.Name)
       If CultureInfo.CurrentCulture.Name = "fr-FR" Then
          Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US")
       Else
          Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR")
       End If
       Console.WriteLine("Changed the current culture to {0}.",
                         CultureInfo.CurrentCulture.Name)
       Console.WriteLine()                  
       
       ' Execute the delegate synchronously.
       Console.WriteLine("Executing the delegate synchronously:")
       Console.WriteLine(formatDelegate())
       
       ' Call an async delegate to format the values using one format string.
       Console.WriteLine("Executing a task asynchronously:") 
       Dim t1 = Task.Run(formatDelegate)
       Console.WriteLine(t1.Result)
       
       Console.WriteLine("Executing a task synchronously:")
       Dim t2 = New Task(Of String)(formatDelegate) 
       t2.RunSynchronously()
       Console.WriteLine(t2.Result)
   End Sub
End Module
' The example displays the following output:
'          The example is running on thread 1
'          The current culture is en-US
'          Changed the current culture to fr-FR.
'
'          Executing the delegate synchronously:
'          Formatting Imports the fr-FR culture on thread 1.
'          163 025 412,32 €   18 905 365,59 €
'
'          Executing a task asynchronously:
'          Formatting Imports the fr-FR culture on thread 3.
'          163 025 412,32 €   18 905 365,59 €
'
'          Executing a task synchronously:
'          Formatting Imports the fr-FR culture on thread 1.
'          163 025 412,32 €   18 905 365,59 €
' If the TargetFrameworkAttribute statement is removed, the example
' displays the following output:
'          The example is running on thread 1
'          The current culture is en-US
'          Changed the current culture to fr-FR.
'
'          Executing the delegate synchronously:
'          Formatting using the fr-FR culture on thread 1.
'          163 025 412,32 €   18 905 365,59 €
'
'          Executing a task asynchronously:
'          Formatting using the en-US culture on thread 3.
'          $163,025,412.32   $18,905,365.59
'
'          Executing a task synchronously:
'          Formatting using the fr-FR culture on thread 1.
'          163 025 412,32 €   18 905 365,59 €

Si utiliza Visual Studio, puede omitir el atributo TargetFrameworkAttribute y seleccionar en su lugar .NET Framework 4.6 como destino al crear el proyecto en el cuadro de diálogo Nuevo proyecto.If you are using Visual Studio, you can omit the TargetFrameworkAttribute attribute and instead select the .NET Framework 4.6 as the target when you create the project in the New Project dialog.

Para que la salida refleje el comportamiento de las aplicaciones destinadas a versiones de .NET Framework anteriores a .NET Framework 4.6, quite el atributo TargetFrameworkAttribute del código fuente.For output that reflects the behavior of apps the target versions of the .NET Framework prior to .NET Framework 4.6, remove the TargetFrameworkAttribute attribute from the source code. La salida reflejará las convenciones de formato de la referencia cultural predeterminada del sistema, no la referencia cultural del subproceso que realiza la llamada.The output will reflect the formatting conventions of the default system culture, not the culture of the calling thread.

Para obtener más información sobre las tareas asincrónicas y la referencia cultural, consulte la sección "Referencia cultural y operaciones asincrónicas basadas en tareas" en el tema CultureInfo.For more information on asynchronous tasks and culture, see the "Culture and asynchronous task-based operations" section in the CultureInfo topic.

Crear continuaciones de tareasCreating task continuations

Los métodos Task.ContinueWith y Task<TResult>.ContinueWith permiten especificar una tarea que se iniciará cuando finalice la tarea anterior.The Task.ContinueWith and Task<TResult>.ContinueWith methods let you specify a task to start when the antecedent task finishes. Al delegado de la tarea de continuación se le pasa una referencia a la tarea antecedente para que pueda examinar el estado de dicha tarea y, al recuperar el valor de la propiedad Task<TResult>.Result, puede usar la salida del antecedente como entrada de la continuación.The delegate of the continuation task is passed a reference to the antecedent task so that it can examine the antecedent task's status and, by retrieving the value of the Task<TResult>.Result property, can use the output of the antecedent as input for the continuation.

En el ejemplo siguiente, la tarea getData se inicia llamando al método TaskFactory.StartNew<TResult>(Func<TResult>).In the following example, the getData task is started by a call to the TaskFactory.StartNew<TResult>(Func<TResult>) method. La tarea processData se inicia automáticamente cuando finaliza getData y displayData se inicia cuando finaliza processData.The processData task is started automatically when getData finishes, and displayData is started when processData finishes. getData genera una matriz de enteros, accesible a la tarea processData mediante la propiedad getData de la tarea Task<TResult>.Result.getData produces an integer array, which is accessible to the processData task through the getData task's Task<TResult>.Result property. La tarea processData procesa dicha matriz y devuelve un resultado cuyo tipo se deduce del tipo de valor devuelto de la expresión lambda pasada al método Task<TResult>.ContinueWith<TNewResult>(Func<Task<TResult>,TNewResult>).The processData task processes that array and returns a result whose type is inferred from the return type of the lambda expression passed to the Task<TResult>.ContinueWith<TNewResult>(Func<Task<TResult>,TNewResult>) method. La tarea displayData se ejecuta automáticamente cuando finaliza processData y el objeto Tuple<T1,T2,T3> devuelto por la expresión lambda processData es accesible para la tarea displayData mediante la propiedad processData de la tarea Task<TResult>.Result.The displayData task executes automatically when processData finishes, and the Tuple<T1,T2,T3> object returned by the processData lambda expression is accessible to the displayData task through the processData task's Task<TResult>.Result property. La tarea displayData toma el resultado de la tarea processData y genera un resultado cuyo tipo se deduce de forma similar y que se pone a disposición del programa en la propiedad Result.The displayData task takes the result of the processData task and produces a result whose type is inferred in a similar manner and which is made available to the program in the Result property.

using System;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {                         
      var getData = Task.Factory.StartNew(() => { 
                                             Random rnd = new Random(); 
                                             int[] values = new int[100];
                                             for (int ctr = 0; ctr <= values.GetUpperBound(0); ctr++)
                                                values[ctr] = rnd.Next();

                                             return values;
                                          } );  
      var processData = getData.ContinueWith((x) => {
                                                int n = x.Result.Length;
                                                long sum = 0;
                                                double mean;
                                  
                                                for (int ctr = 0; ctr <= x.Result.GetUpperBound(0); ctr++)
                                                   sum += x.Result[ctr];

                                                mean = sum / (double) n;
                                                return Tuple.Create(n, sum, mean);
                                             } ); 
      var displayData = processData.ContinueWith((x) => {
                                                    return String.Format("N={0:N0}, Total = {1:N0}, Mean = {2:N2}",
                                                                         x.Result.Item1, x.Result.Item2, 
                                                                         x.Result.Item3);
                                                 } );                         
      Console.WriteLine(displayData.Result);
   }
}
// The example displays output similar to the following:
//    N=100, Total = 110,081,653,682, Mean = 1,100,816,536.82
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
      Dim getData = Task.Factory.StartNew(Function() 
                                             Dim rnd As New Random()
                                             Dim values(99) As Integer
                                             For ctr = 0 To values.GetUpperBound(0)
                                                values(ctr) = rnd.Next()
                                             Next
                                             Return values
                                          End Function)  
      Dim processData = getData.ContinueWith(Function(x)
                                                Dim n As Integer = x.Result.Length
                                                Dim sum As Long
                                                Dim mean As Double
                                  
                                                For ctr = 0 To x.Result.GetUpperBound(0)
                                                   sum += x.Result(ctr)
                                                Next
                                                mean = sum / n
                                                Return Tuple.Create(n, sum, mean)
                                             End Function) 
      Dim displayData = processData.ContinueWith(Function(x)
                                                    Return String.Format("N={0:N0}, Total = {1:N0}, Mean = {2:N2}",
                                                                         x.Result.Item1, x.Result.Item2, 
                                                                         x.Result.Item3)
                                                 End Function)                         
      Console.WriteLine(displayData.Result)
   End Sub                        
End Module
' The example displays output like the following:
'   N=100, Total = 110,081,653,682, Mean = 1,100,816,536.82

Dado que Task.ContinueWith es un método de instancia, puede encadenar llamadas a métodos en lugar de crear instancias de un objeto Task<TResult> para cada tarea antecedente.Because Task.ContinueWith is an instance method, you can chain method calls together instead of instantiating a Task<TResult> object for each antecedent task. El ejemplo siguiente es funcionalmente idéntico al anterior, con la diferencia de que encadena llamadas al método Task.ContinueWith.The following example is functionally identical to the previous example, except that it chains together calls to the Task.ContinueWith method. Observe que el objeto Task<TResult> devuelto por la cadena de llamadas al método es la tarea final de continuación.Note that the Task<TResult> object returned by the chain of method calls is the final continuation task.

using System;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {                         
      var displayData = Task.Factory.StartNew(() => { 
                                                 Random rnd = new Random(); 
                                                 int[] values = new int[100];
                                                 for (int ctr = 0; ctr <= values.GetUpperBound(0); ctr++)
                                                    values[ctr] = rnd.Next();

                                                 return values;
                                              } ).  
                        ContinueWith((x) => {
                                        int n = x.Result.Length;
                                        long sum = 0;
                                        double mean;
                                  
                                        for (int ctr = 0; ctr <= x.Result.GetUpperBound(0); ctr++)
                                           sum += x.Result[ctr];

                                        mean = sum / (double) n;
                                        return Tuple.Create(n, sum, mean);
                                     } ). 
                        ContinueWith((x) => {
                                        return String.Format("N={0:N0}, Total = {1:N0}, Mean = {2:N2}",
                                                             x.Result.Item1, x.Result.Item2, 
                                                             x.Result.Item3);
                                     } );                         
      Console.WriteLine(displayData.Result);
   }
}
// The example displays output similar to the following:
//    N=100, Total = 110,081,653,682, Mean = 1,100,816,536.82
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
      Dim displayData = Task.Factory.StartNew(Function() 
                                                 Dim rnd As New Random()
                                                 Dim values(99) As Integer
                                                 For ctr = 0 To values.GetUpperBound(0)
                                                    values(ctr) = rnd.Next()
                                                 Next
                                                 Return values
                                              End Function). _  
                        ContinueWith(Function(x)
                                        Dim n As Integer = x.Result.Length
                                        Dim sum As Long
                                        Dim mean As Double
                                  
                                        For ctr = 0 To x.Result.GetUpperBound(0)
                                           sum += x.Result(ctr)
                                        Next
                                        mean = sum / n
                                        Return Tuple.Create(n, sum, mean)
                                     End Function). _ 
                        ContinueWith(Function(x)
                                        Return String.Format("N={0:N0}, Total = {1:N0}, Mean = {2:N2}",
                                                             x.Result.Item1, x.Result.Item2,  
                                                             x.Result.Item3)
                                     End Function)                         
      Console.WriteLine(displayData.Result)
   End Sub                        
End Module
' The example displays output like the following:
'   N=100, Total = 110,081,653,682, Mean = 1,100,816,536.82

Los métodos ContinueWhenAll y ContinueWhenAny permiten continuar a partir de varias tareas.The ContinueWhenAll and ContinueWhenAny methods enable you to continue from multiple tasks.

Para más información, consulte Chaining Tasks by Using Continuation Tasks (Encadenar tareas mediante tareas de continuación).For more information, see Chaining Tasks by Using Continuation Tasks.

Crear tareas secundarias desasociadasCreating detached child tasks

Cuando el código de usuario que se está ejecutando en una tarea crea una nueva tarea y no especifica la opción AttachedToParent, la nueva tarea no se sincroniza con la tarea principal de ninguna manera especial.When user code that is running in a task creates a new task and does not specify the AttachedToParent option, the new task is not synchronized with the parent task in any special way. Este tipo de tarea no sincronizada se denomina tarea anidada desasociada o tarea secundaria desasociada.This type of non-synchronized task is called a detached nested task or detached child task. En el siguiente ejemplo se muestra una tarea que crea una tarea secundaria desasociada.The following example shows a task that creates one detached child task.

var outer = Task.Factory.StartNew(() =>
{
    Console.WriteLine("Outer task beginning.");

    var child = Task.Factory.StartNew(() =>
    {
        Thread.SpinWait(5000000);
        Console.WriteLine("Detached task completed.");
    });

});

outer.Wait();
Console.WriteLine("Outer task completed.");
// The example displays the following output:
//    Outer task beginning.
//    Outer task completed.
//    Detached task completed.
Dim outer = Task.Factory.StartNew(Sub()
                                      Console.WriteLine("Outer task beginning.")
                                      Dim child = Task.Factory.StartNew(Sub()
                                                                            Thread.SpinWait(5000000)
                                                                            Console.WriteLine("Detached task completed.")
                                                                        End Sub)
                                  End Sub)
outer.Wait()
Console.WriteLine("Outer task completed.")
' The example displays the following output:
'     Outer task beginning.
'     Outer task completed.
'    Detached child completed.

Observe que la tarea primaria no espera a que la tarea secundaria desasociada se complete.Note that the parent task does not wait for the detached child task to finish.

Crear tareas secundariasCreating child tasks

Cuando el código de usuario que se está ejecutando en una tarea crea una tarea con la opción AttachedToParent, la nueva tarea se conoce como una tarea secundaria asociada de la tarea principal.When user code that is running in a task creates a task with the AttachedToParent option, the new task is known as a attached child task of the parent task. Puede usar la opción AttachedToParent para expresar el paralelismo de tareas estructurado, ya que la tarea primaria espera implícitamente a que todas las tareas secundarias asociadas finalicen.You can use the AttachedToParent option to express structured task parallelism, because the parent task implicitly waits for all attached child tasks to finish. En el ejemplo siguiente se muestra una tarea principal que crea diez tareas secundarias adjuntas.The following example shows a parent task that creates ten attached child tasks. Observe que aunque en el ejemplo se llame al método Task.Wait para esperar a que la tarea primaria finalice, no tiene que esperar explícitamente a que se completen las tareas secundarias asociadas.Note that although the example calls the Task.Wait method to wait for the parent task to finish, it does not have to explicitly wait for the attached child tasks to complete.

using System;
using System.Threading;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      var parent = Task.Factory.StartNew(() => {
                      Console.WriteLine("Parent task beginning.");
                      for (int ctr = 0; ctr < 10; ctr++) {
                         int taskNo = ctr;
                         Task.Factory.StartNew((x) => {
                                                  Thread.SpinWait(5000000);
                                                  Console.WriteLine("Attached child #{0} completed.", 
                                                                    x);
                                               },
                                               taskNo, TaskCreationOptions.AttachedToParent);
                      }
                   });

      parent.Wait();
      Console.WriteLine("Parent task completed.");
   }
}
// The example displays output like the following:
//       Parent task beginning.
//       Attached child #9 completed.
//       Attached child #0 completed.
//       Attached child #8 completed.
//       Attached child #1 completed.
//       Attached child #7 completed.
//       Attached child #2 completed.
//       Attached child #6 completed.
//       Attached child #3 completed.
//       Attached child #5 completed.
//       Attached child #4 completed.
//       Parent task completed.
Imports System.Threading
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
      Dim parent = Task.Factory.StartNew(Sub()
                                            Console.WriteLine("Parent task beginning.")
                                            For ctr As Integer = 0 To 9
                                               Dim taskNo As Integer = ctr
                                               Task.Factory.StartNew(Sub(x)
                                                                        Thread.SpinWait(5000000)
                                                                        Console.WriteLine("Attached child #{0} completed.", 
                                                                                          x)
                                                                     End Sub,
                                                                     taskNo, TaskCreationOptions.AttachedToParent)
                                            Next
                                         End Sub)
      parent.Wait()
      Console.WriteLine("Parent task completed.")
   End Sub
End Module   
' The example displays output like the following:
'       Parent task beginning.
'       Attached child #9 completed.
'       Attached child #0 completed.
'       Attached child #8 completed.
'       Attached child #1 completed.
'       Attached child #7 completed.
'       Attached child #2 completed.
'       Attached child #6 completed.
'       Attached child #3 completed.
'       Attached child #5 completed.
'       Attached child #4 completed.
'       Parent task completed.

Una tarea principal puede usar la opción TaskCreationOptions.DenyChildAttach para evitar que otras tareas se asocien a la tarea primaria.A parent task can use the TaskCreationOptions.DenyChildAttach option to prevent other tasks from attaching to the parent task. Para más información, consulte Attached and Detached Child Tasks (Tareas secundarias asociadas y desasociadas).For more information, see Attached and Detached Child Tasks.

Esperar hasta que las tareas finalicenWaiting for tasks to finish

Los tipos System.Threading.Tasks.Task y System.Threading.Tasks.Task<TResult> proporcionan varias sobrecargas de los métodos Task.Wait que permiten esperar a que finalice una tarea.The System.Threading.Tasks.Task and System.Threading.Tasks.Task<TResult> types provide several overloads of the Task.Wait methods that enable you to wait for a task to finish. Además, las sobrecargas del método Task.WaitAll estático y del método Task.WaitAny permiten esperar a que finalicen alguna o todas las tareas de una matriz de tareas.In addition, overloads of the static Task.WaitAll and Task.WaitAny methods let you wait for any or all of an array of tasks to finish.

Normalmente, una tarea se espera por una de estas razones:Typically, you would wait for a task for one of these reasons:

  • El subproceso principal depende del resultado final que se calcula mediante una tarea.The main thread depends on the final result computed by a task.

  • Hay que controlar las excepciones que pueden producirse en la tarea.You have to handle exceptions that might be thrown from the task.

  • La aplicación puede finalizar antes de que todas las tareas hayan terminado de ejecutarse.The application may terminate before all tasks have completed execution. Por ejemplo, las aplicaciones de consola finalizarán en cuanto se ejecute todo el código sincrónico en Main (el punto de entrada de la aplicación).For example, console applications will terminate as soon as all synchronous code in Main (the application entry point) has executed.

En el siguiente ejemplo se muestra el modelo básico donde el control de excepciones no está implicado.The following example shows the basic pattern that does not involve exception handling.

Task[] tasks = new Task[3]
{
    Task.Factory.StartNew(() => MethodA()),
    Task.Factory.StartNew(() => MethodB()),
    Task.Factory.StartNew(() => MethodC())
};

//Block until all tasks complete.
Task.WaitAll(tasks);

// Continue on this thread...
Dim tasks() =
{
    Task.Factory.StartNew(Sub() MethodA()),
    Task.Factory.StartNew(Sub() MethodB()),
    Task.Factory.StartNew(Sub() MethodC())
}

' Block until all tasks complete.
Task.WaitAll(tasks)

' Continue on this thread...

Para un ejemplo que muestra el control de excepciones, consulte Control de excepciones.For an example that shows exception handling, see Exception Handling.

Algunas sobrecargas permiten especificar un tiempo de espera, mientras que otras toman un objeto CancellationToken adicional como parámetro de entrada, de modo que la espera puede cancelarse mediante programación o en respuesta a los datos proporcionados por el usuario.Some overloads let you specify a time-out, and others take an additional CancellationToken as an input parameter, so that the wait itself can be canceled either programmatically or in response to user input.

Cuando se espera a una tarea, se espera implícitamente a todos los elementos secundarios de esa tarea que se crearon con la opción TaskCreationOptions.AttachedToParent.When you wait for a task, you implicitly wait for all children of that task that were created by using the TaskCreationOptions.AttachedToParent option. Task.Wait devuelve un valor inmediatamente si la tarea ya se ha completado.Task.Wait returns immediately if the task has already completed. Un método Task.Wait producirá las tareas generadas por una tarea incluso si se llama a este método Task.Wait una vez completada la tarea.Any exceptions raised by a task will be thrown by a Task.Wait method, even if the Task.Wait method was called after the task completed.

Componer tareasComposing tasks

Las clases Task y Task<TResult> proporcionan varios métodos que pueden ayudar a componer varias tareas para implementar patrones comunes y para mejorar el uso de las características de lenguaje asincrónicas proporcionadas por C#, Visual Basic y F#.The Task and Task<TResult> classes provide several methods that can help you compose multiple tasks to implement common patterns and to better use the asynchronous language features that are provided by C#, Visual Basic, and F#. En esta sección se describen los métodos WhenAll, WhenAny, Delay y FromResult.This section describes the WhenAll, WhenAny, Delay, and FromResult methods.

Task.WhenAllTask.WhenAll

El método Task.WhenAll espera de forma asincrónica a que finalicen varios Task objetos Task<TResult>.The Task.WhenAll method asynchronously waits for multiple Task or Task<TResult> objects to finish. Proporciona versiones sobrecargadas que permiten esperar a conjuntos no uniformes de tareas.It provides overloaded versions that enable you to wait for non-uniform sets of tasks. Por ejemplo, puede esperar a que varios objetos Task y Task<TResult> se completen de una llamada al método.For example, you can wait for multiple Task and Task<TResult> objects to complete from one method call.

Task.WhenAnyTask.WhenAny

El método Task.WhenAny espera de forma asincrónica a que finalice uno de varios Task objetos Task<TResult>.The Task.WhenAny method asynchronously waits for one of multiple Task or Task<TResult> objects to finish. Como en el método Task.WhenAll, este método proporciona versiones sobrecargadas que permiten esperar a conjuntos no uniformes de tareas.As in the Task.WhenAll method, this method provides overloaded versions that enable you to wait for non-uniform sets of tasks. El método WhenAny es especialmente útil en los siguientes escenarios.The WhenAny method is especially useful in the following scenarios.

  • Operaciones redundantes.Redundant operations. Considere un algoritmo o una operación que pueda realizarse de muchas maneras.Consider an algorithm or operation that can be performed in many ways. Puede usar el método WhenAny para seleccionar la operación que finaliza primero y luego cancelar las operaciones restantes.You can use the WhenAny method to select the operation that finishes first and then cancel the remaining operations.

  • Operaciones intercaladas.Interleaved operations. Puede iniciar varias operaciones, todas las cuales deben finalizar y utilizar el método WhenAny para procesar los resultados cuando finalice cada operación.You can start multiple operations that must all finish and use the WhenAny method to process results as each operation finishes. Finalizada una operación, puede iniciar una o más tareas adicionales.After one operation finishes, you can start one or more additional tasks.

  • Operaciones limitadas.Throttled operations. Puede usar el método WhenAny para extender el escenario anterior limitando el número de operaciones simultáneas.You can use the WhenAny method to extend the previous scenario by limiting the number of concurrent operations.

  • Operaciones que han expirado.Expired operations. Puede usar el método WhenAny para seleccionar entre una o más tareas y una tarea que termina después de un tiempo concreto, como una tarea devuelta por el método Delay.You can use the WhenAny method to select between one or more tasks and a task that finishes after a specific time, such as a task that is returned by the Delay method. El método Delay se describe en la sección siguiente.The Delay method is described in the following section.

Task.DelayTask.Delay

El método Task.Delay produce un objeto Task que finaliza tras el tiempo especificado.The Task.Delay method produces a Task object that finishes after the specified time. Puede usar este método para crear bucles que sondeen ocasionalmente en busca de datos, introduzcan finales de tiempo de espera, retrasen el control de los datos proporcionados por el usuario durante un tiempo predeterminado, etc.You can use this method to build loops that occasionally poll for data, introduce time-outs, delay the handling of user input for a predetermined time, and so on.

Task(T).FromResultTask(T).FromResult

Mediante el método Task.FromResult, puede crear un objeto Task<TResult> que contenga un resultado previamente calculado.By using the Task.FromResult method, you can create a Task<TResult> object that holds a pre-computed result. Este método es útil cuando se realiza una operación asincrónica que devuelve un objeto Task<TResult> y el resultado de ese objeto Task<TResult> ya se ha calculado.This method is useful when you perform an asynchronous operation that returns a Task<TResult> object, and the result of that Task<TResult> object is already computed. Para obtener un ejemplo en el que se usa FromResult para recuperar los resultados de las operaciones de descarga asincrónica que se retienen en caché, vea Procedimiento para crear tareas precalculadas.For an example that uses FromResult to retrieve the results of asynchronous download operations that are held in a cache, see How to: Create Pre-Computed Tasks.

Controlar excepciones en tareasHandling exceptions in tasks

Cuando una tarea produce una o más excepciones, las excepciones se encapsulan en una excepción AggregateException.When a task throws one or more exceptions, the exceptions are wrapped in an AggregateException exception. Esa excepción se propaga de nuevo al subproceso que se combina con la tarea, que normalmente es el subproceso que está esperando a que la tarea termine o al subproceso que tiene acceso a la propiedad Result.That exception is propagated back to the thread that joins with the task, which is typically the thread that is waiting for the task to finish or the thread that accesses the Result property. Este comportamiento sirve para aplicar la directiva de .NET Framework por la que, de manera predeterminada, todas las excepciones no controladas deben terminar el proceso.This behavior serves to enforce the .NET Framework policy that all unhandled exceptions by default should terminate the process. El código de llamada puede controlar las excepciones con cualquiera de los siguientes elementos del bloque try/catch:The calling code can handle the exceptions by using any of the following in a try/catch block:

El subproceso de unión también puede controlar excepciones; para ello, obtiene acceso a la propiedad Exception antes de que la tarea se recolecte como elemento no utilizado.The joining thread can also handle exceptions by accessing the Exception property before the task is garbage-collected. Al obtener acceso a esta propiedad, impide que la excepción no controlada desencadene el comportamiento de propagación de la excepción que termina el proceso cuando el objeto ha finalizado.By accessing this property, you prevent the unhandled exception from triggering the exception propagation behavior that terminates the process when the object is finalized.

Para más información sobre las excepciones y las tareas, consulte Control de excepciones.For more information about exceptions and tasks, see Exception Handling.

Cancelar tareasCanceling tasks

La clase Task admite la cancelación cooperativa y está completamente integrada con las clases System.Threading.CancellationTokenSource y System.Threading.CancellationToken, que se presentaron en .NET Framework versión 4.The Task class supports cooperative cancellation and is fully integrated with the System.Threading.CancellationTokenSource and System.Threading.CancellationToken classes, which were introduced in the .NET Framework 4. Muchos de los constructores de la clase System.Threading.Tasks.Task toman un objeto CancellationToken como parámetro de entrada.Many of the constructors in the System.Threading.Tasks.Task class take a CancellationToken object as an input parameter. Muchas de las sobrecargas de StartNew y Run incluyen también un parámetro CancellationToken.Many of the StartNew and Run overloads also include a CancellationToken parameter.

Puede crear el token y emitir la solicitud de cancelación posteriormente usando la clase CancellationTokenSource.You can create the token, and issue the cancellation request at some later time, by using the CancellationTokenSource class. A continuación, debe pasar el token a Task como argumento y hacer referencia al mismo token también en el delegado de usuario, que se encarga de responder a una solicitud de cancelación.Pass the token to the Task as an argument, and also reference the same token in your user delegate, which does the work of responding to a cancellation request.

Para más información, vea Cancelación de tareas y Cómo: Cancelar una tarea y sus elementos secundarios.For more information, see Task Cancellation and How to: Cancel a Task and Its Children.

Clase TaskFactoryThe TaskFactory class

La clase TaskFactory proporciona métodos estáticos que encapsulan algunos modelos comunes de creación e inicio de tareas y tareas de continuación.The TaskFactory class provides static methods that encapsulate some common patterns for creating and starting tasks and continuation tasks.

Al objeto TaskFactory predeterminado se puede tener acceso como propiedad estática de la clase Task o de la clase Task<TResult>.The default TaskFactory can be accessed as a static property on the Task class or Task<TResult> class. También pueden crearse directamente instancias de TaskFactory y especificar varias opciones entre las que se incluyan las opciones CancellationToken, TaskCreationOptions, TaskContinuationOptions o TaskScheduler.You can also instantiate a TaskFactory directly and specify various options that include a CancellationToken, a TaskCreationOptions option, a TaskContinuationOptions option, or a TaskScheduler. Cualquier opción que se especifique al crear el generador de tareas se aplicará a todas las tareas que este generador cree, a menos que Task se cree usando la enumeración TaskCreationOptions, en cuyo caso las opciones de la tarea reemplazarán a las del generador de tareas.Whatever options are specified when you create the task factory will be applied to all tasks that it creates, unless the Task is created by using the TaskCreationOptions enumeration, in which case the task's options override those of the task factory.

Tareas sin delegadosTasks without delegates

En algunos casos, es posible que desee usar un objeto Task para encapsular alguna operación asincrónica ejecutada por un componente externo en lugar de su propio usuario delegado.In some cases, you may want to use a Task to encapsulate some asynchronous operation that is performed by an external component instead of your own user delegate. Si la operación se basa en el patrón Begin/End del modelo de programación asincrónica, puede usar los métodos FromAsync.If the operation is based on the Asynchronous Programming Model Begin/End pattern, you can use the FromAsync methods. Si no es este el caso, puede usar el objeto TaskCompletionSource<TResult> para encapsular la operación en una tarea y, de este modo, aprovechar algunas de las ventajas de programación de Task, como por ejemplo, su compatibilidad con la propagación de excepciones y el uso de continuaciones.If that is not the case, you can use the TaskCompletionSource<TResult> object to wrap the operation in a task and thereby gain some of the benefits of Task programmability, for example, support for exception propagation and continuations. Para más información, consulte TaskCompletionSource<TResult>.For more information, see TaskCompletionSource<TResult>.

Programadores personalizadosCustom schedulers

La mayoría de los desarrolladores de aplicaciones o bibliotecas no prestan atención al procesador en el que se ejecuta la tarea, al modo en que la tarea sincroniza su trabajo con otras tareas o al modo en que se programa la tarea en el objeto System.Threading.ThreadPool.Most application or library developers do not care which processor the task runs on, how it synchronizes its work with other tasks, or how it is scheduled on the System.Threading.ThreadPool. Solo necesitan que la ejecución en el equipo host sea lo más eficaz posible.They only require that it execute as efficiently as possible on the host computer. Si necesita tener un control más minucioso sobre los detalles de programación, la biblioteca TPL permite configurar algunos valores del programador de tareas predeterminado e incluso permite proporcionar un programador personalizado.If you require more fine-grained control over the scheduling details, the Task Parallel Library lets you configure some settings on the default task scheduler, and even lets you supply a custom scheduler. Para más información, consulte TaskScheduler.For more information, see TaskScheduler.

TPL tiene varios tipos públicos nuevos que resultan útiles tanto en escenarios en paralelo como en escenarios secuenciales.The TPL has several new public types that are useful in both parallel and sequential scenarios. Entre ellos, se incluyen diversas clases de colecciones multiproceso rápidas y escalables del espacio de nombres System.Collections.Concurrent y varios tipos nuevos de sincronización, como System.Threading.Semaphore y System.Threading.ManualResetEventSlim, que resultan más eficaces que sus predecesores en tipos concretos de cargas de trabajo.These include several thread-safe, fast and scalable collection classes in the System.Collections.Concurrent namespace, and several new synchronization types, for example, System.Threading.Semaphore and System.Threading.ManualResetEventSlim, which are more efficient than their predecessors for specific kinds of workloads. Otros tipos nuevos de .NET Framework versión 4, como System.Threading.Barrier y System.Threading.SpinLock, proporcionan una funcionalidad que no estaba disponible en versiones anteriores.Other new types in the .NET Framework 4, for example, System.Threading.Barrier and System.Threading.SpinLock, provide functionality that was not available in earlier releases. Para más información, consulte Data Structures for Parallel Programming (Estructuras de datos para la programación en paralelo).For more information, see Data Structures for Parallel Programming.

Tipos de tarea personalizadosCustom task types

Se recomienda no heredar de System.Threading.Tasks.Task ni de System.Threading.Tasks.Task<TResult>.We recommend that you do not inherit from System.Threading.Tasks.Task or System.Threading.Tasks.Task<TResult>. En su lugar, se recomienda usar la propiedad AsyncState para asociar los datos adicionales o el estado a un objeto Task o Task<TResult>.Instead, we recommend that you use the AsyncState property to associate additional data or state with a Task or Task<TResult> object. También puede usar métodos de extensión para extender la funcionalidad de las clases Task y Task<TResult>.You can also use extension methods to extend the functionality of the Task and Task<TResult> classes. Para más información sobre los métodos de extensión, consulte Extension Methods (Métodos de extensión) y Extension Methods (Métodos de extensión).For more information about extension methods, see Extension Methods and Extension Methods.

Si debe heredar de Task o Task<TResult>, no puede usar las clases Run, Run, o System.Threading.Tasks.TaskFactory, System.Threading.Tasks.TaskFactory<TResult>, o System.Threading.Tasks.TaskCompletionSource<TResult> para crear instancias del tipo de tarea personalizada porque estos mecanismos solo crean objetos Task y objetos Task<TResult>.If you must inherit from Task or Task<TResult>, you cannot use Run, Run, or the System.Threading.Tasks.TaskFactory, System.Threading.Tasks.TaskFactory<TResult>, or System.Threading.Tasks.TaskCompletionSource<TResult> classes to create instances of your custom task type because these mechanisms create only Task and Task<TResult> objects. Además, no puede usar los mecanismos de continuación de tarea proporcionados por Task, Task<TResult>, TaskFactory y TaskFactory<TResult> para crear instancias del tipo de tarea personalizada porque también estos mecanismos crean solo objetos Task y Task<TResult>.In addition, you cannot use the task continuation mechanisms that are provided by Task, Task<TResult>, TaskFactory, and TaskFactory<TResult> to create instances of your custom task type because these mechanisms also create only Task and Task<TResult> objects.

TitleTitle DESCRIPCIÓNDescription
Chaining Tasks by Using Continuation Tasks (Encadenar tareas mediante tareas de continuación)Chaining Tasks by Using Continuation Tasks Describe el funcionamiento de las continuaciones.Describes how continuations work.
Attached and Detached Child Tasks (Tareas secundarias asociadas y desasociadas)Attached and Detached Child Tasks Describe la diferencia entre las tareas secundarias asociadas y desasociadas.Describes the difference between attached and detached child tasks.
Cancelación de tareasTask Cancellation Describe la compatibilidad con la cancelación que está integrada en el objeto Task.Describes the cancellation support that is built into the Task object.
Control de excepcionesException Handling Describe cómo se controlan excepciones en subprocesos simultáneos.Describes how exceptions on concurrent threads are handled.
Cómo: Usar Parallel.Invoke para ejecutar operaciones en paraleloHow to: Use Parallel.Invoke to Execute Parallel Operations Describe cómo usar Invoke.Describes how to use Invoke.
Cómo: Devolver un valor a partir de una tareaHow to: Return a Value from a Task Describe cómo devolver valores de tareas.Describes how to return values from tasks.
Cómo: Cancelar una tarea y sus elementos secundariosHow to: Cancel a Task and Its Children Describe cómo cancelar tareas.Describes how to cancel tasks.
Cómo: Crear tareas precalculadasHow to: Create Pre-Computed Tasks Describe cómo utilizar el método Task.FromResult para recuperar los resultados de las operaciones asincrónicas de descarga que se retienen en una memoria caché.Describes how to use the Task.FromResult method to retrieve the results of asynchronous download operations that are held in a cache.
Cómo: Recorrer un árbol binario con tareas en paraleloHow to: Traverse a Binary Tree with Parallel Tasks Describe cómo utilizar tareas para atravesar un árbol binario.Describes how to use tasks to traverse a binary tree.
Cómo: Desencapsular una tarea anidadaHow to: Unwrap a Nested Task Demuestra cómo utilizar el método de extensión Unwrap.Demonstrates how to use the Unwrap extension method.
Data Parallelism (Paralelismo de datos)Data Parallelism Describe cómo usar For y ForEach para crear bucles paralelos sobre los datos.Describes how to use For and ForEach to create parallel loops over data.
Programación en paraleloParallel Programming Nodo de nivel superior de la programación en paralelo de .NET Framework.Top level node for .NET Framework parallel programming.

Vea tambiénSee also