Асинхронное программирование на основе задачTask-based Asynchronous Programming

Библиотека параллельных задач (TPL) основана на концепции задач, представляющих асинхронные операции.The Task Parallel Library (TPL) is based on the concept of a task, which represents an asynchronous operation. В некотором смысле задача похожа на поток или рабочий элемент ThreadPool, но на более высоком уровне абстракции.In some ways, a task resembles a thread or ThreadPool work item, but at a higher level of abstraction. Термин параллелизм задач означает одновременное выполнение одной или нескольких разных задач.The term task parallelism refers to one or more independent tasks running concurrently. Задачи предоставляют два основных преимущества.Tasks provide two primary benefits:

  • Более эффективное и масштабируемое использование системных ресурсов.More efficient and more scalable use of system resources.

    В фоновом режиме задачи помещаются в очередь ThreadPool, усовершенствованную с помощью алгоритмов, которые определяют и настраивают количество потоков и обеспечивают балансировку нагрузки для повышения производительности.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. Это делает задачи относительно простыми и позволяет создавать множество задач для использования точного параллелизма.This makes tasks relatively lightweight, and you can create many of them to enable fine-grained parallelism.

  • Больший программный контроль по сравнению с потоком или рабочим элементом.More programmatic control than is possible with a thread or work item.

    Задачи и построение платформы на их основе предоставляют богатый набор интерфейсов API, которые поддерживают ожидание, отмену, продолжения, надежную обработку исключений, подробные состояния, пользовательское планирование и многое другое.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.

По этим причинам TPL в .NET Framework — предпочтительный API для написания многопоточного асинхронного параллельного кода.For both of these reasons, in the .NET Framework, TPL is the preferred API for writing multi-threaded, asynchronous, and parallel code.

Неявное создание и запуск задачCreating and running tasks implicitly

Метод Parallel.Invoke предоставляет удобный способ одновременного запуска любого числа произвольных операторов.The Parallel.Invoke method provides a convenient way to run any number of arbitrary statements concurrently. Достаточно передать в делегат Action для каждого рабочего элемента.Just pass in an Action delegate for each item of work. Самым простым способом создания этих делегатов является использование лямбда-выражений.The easiest way to create these delegates is to use lambda expressions. Лямбда-выражение может вызвать именованный метод или предоставить встроенный код.The lambda expression can either call a named method or provide the code inline. В следующем примере показан вызов базового метода Invoke, который создает и запускается две задачи, выполняемые параллельно.The following example shows a basic Invoke call that creates and starts two tasks that run concurrently. Первая задача представляется лямбда-выражением, вызывающим метод DoSomeWork, а вторая — лямбда-выражением, вызывающим метод 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.

Примечание

В этой документации для определения делегатов в библиотеке параллельных задач используются лямбда-выражения.This documentation uses lambda expressions to define delegates in TPL. Если вы не знакомы с лямбда-выражениями в C# или Visual Basic, см. раздел Лямбда-выражения в PLINQ и 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())

Примечание

Число экземпляров Task, созданных Invoke в фоновом режиме, не обязательно равно числу предоставленных делегатов.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. Библиотека параллельных задач может применять различные оптимизации, особенно с большим количеством делегатов.The TPL may employ various optimizations, especially with large numbers of delegates.

Дополнительные сведения см. в разделе Практическое руководство. Использование метода Parallel.Invoke для выполнения простых параллельных задач.For more information, see How to: Use Parallel.Invoke to Execute Parallel Operations.

Для большего контроля над выполнением задач или возврата значения из задачи необходимо более явно работать с объектами Task.For greater control over task execution or to return a value from the task, you have to work with Task objects more explicitly.

Явное создание и запуск задачCreating and running tasks explicitly

Задача, не возвращающая значение, представляется классом System.Threading.Tasks.Task.A task that does not return a value is represented by the System.Threading.Tasks.Task class. Задача, возвращающая значение, представляется классом System.Threading.Tasks.Task<TResult>, унаследованным от Task.A task that returns a value is represented by the System.Threading.Tasks.Task<TResult> class, which inherits from Task. Объект задачи обрабатывает сведения инфраструктуры и предоставляет методы и свойства, доступные из вызывающего потока в течение времени существования задачи.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. Например, можно получить доступ к свойству Status задачи в любое время для определения того, было ли начато ее выполнение, завершилась ли она, была ли отменена или создала исключение.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. Состояние представлено перечислением TaskStatus.The status is represented by a TaskStatus enumeration.

При создании задачи ей передается пользовательский делегат, инкапсулирующий код, который будет выполнять задача.When you create a task, you give it a user delegate that encapsulates the code that the task will execute. Делегат может быть выражен как именованный делегат, анонимный метод или лямбда-выражение.The delegate can be expressed as a named delegate, an anonymous method, or a lambda expression. Лямбда-выражения могут содержать вызов именованного метода, как показано в следующем примере.Lambda expressions can contain a call to a named method, as shown in the following example. Обратите внимание, что в пример включен вызов метода Task.Wait, чтобы убедиться в окончании выполнения задачи до завершения работы приложения консольного режима.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.

Для создания и запуска задачи в одной операции можно также использовать методы Task.Run.You can also use the Task.Run methods to create and start a task in one operation. Для управления задачей методы Run используют планировщик задач по умолчанию независимо от того, какой планировщик связан с текущим потоком.To manage the task, the Run methods use the default task scheduler, regardless of which task scheduler is associated with the current thread. Методы Run — предпочтительный способ создания и запуска задач, если не требуется более жесткий контроль над созданием и планированием задачи.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.

Для создания и запуска задачи в одной операции можно также использовать метод TaskFactory.StartNew.You can also use the TaskFactory.StartNew method to create and start a task in one operation. Используйте этот метод, если нет необходимости разделять создание и планирование и требуются дополнительные параметры создания задач или использование определенного планировщика, а также при необходимости передачи дополнительного состояния задаче, которое можно получить через ее свойство Task.AsyncState, как показано в следующем примере.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 и Task<TResult> предоставляют статическое свойство Factory, возвращающее экземпляр по умолчанию объекта TaskFactory, чтобы можно было вызвать метод как 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(). Кроме того, поскольку в следующем примере задачи относятся к типу System.Threading.Tasks.Task<TResult>, каждая из них имеет открытое свойство Task<TResult>.Result, содержащее результат вычисления.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. Задачи выполняются асинхронно и могут завершиться в любом порядке.The tasks run asynchronously and may complete in any order. При обращении к свойству Result до завершения вычисления оно блокирует вызывающий поток до тех пор, пока значение не станет доступно.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

Дополнительные сведения см. в разделе Практическое руководство. Возвращение значения из задачи.For more information, see How to: Return a Value from a Task.

При использовании лямбда-выражения для создания делегата имеется доступ ко всем переменным, видимым на этом этапе в исходном коде.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. Однако в некоторых случаях, особенно в циклах, лямбда-выражение не перехватывает переменную, как можно было бы ожидать.However, in some cases, most notably within loops, a lambda doesn't capture the variable as expected. Оно только перехватывает окончательное значение, а не значение, изменяющееся после каждой итерации.It only captures the final value, not the value as it mutates after each iteration. В следующем примере показана эта проблема.The following example illustrates the problem. В нем счетчик цикла передается лямбда-выражению, создающему экземпляр объекта CustomData, и используется в качестве идентификатора объекта.It passes a loop counter to a lambda expression that instantiates a CustomData object and uses the loop counter as the object's identifier. Как видно из выходных данных примера, все объекты CustomData имеют одинаковые идентификаторы.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.

Доступ к значению для каждой итерации можно получить, предоставив объект состояния задаче через ее конструктор.You can access the value on each iteration by providing a state object to a task through its constructor. В следующем примере предыдущий пример изменяется путем использования счетчика цикла при создании объекта CustomData, который, в свою очередь, передается лямбда-выражению.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. Как видно из выходных данных примера, каждый объект CustomData теперь имеет уникальный идентификатор, основанный на значении счетчика цикла в момент создания экземпляра объекта.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.

Это состояние передается в качестве аргумента делегату задачи, и доступ к нему можно получить из объекта задачи с помощью свойства 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. В следующем примере представлен вариант предыдущего примера.The following example is a variation on the previous example. В нем используется свойство AsyncState для отображения сведений об объектах CustomData, переданных лямбда-выражению.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

Идентификатор задачиTask ID

Каждая задача получает целочисленный идентификатор, уникально определяющий ее в домене приложения. Доступ к нему можно получить с помощью свойства 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. Этот идентификатор полезен для просмотра сведений о задаче в окнах Параллельные стеки и Задачи отладчика Visual Studio.The ID is useful for viewing task information in the Visual Studio debugger Parallel Stacks and Tasks windows. Он создается только после того, как запрашивается. Поэтому при каждом запуске программы задача может иметь разные идентификаторы.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. Дополнительные сведения о том, как просматривать идентификаторы задач в отладчике, см. в разделах Использование окна задач и Использование окна "Параллельные стеки".For more information about how to view task IDs in the debugger, see Using the Tasks Window and Using the Parallel Stacks Window.

Параметры создания задачиTask Creation Options

Большинство интерфейсов API, в которых создаются задачи, предоставляют перегрузки, принимающие параметр TaskCreationOptions.Most APIs that create tasks provide overloads that accept a TaskCreationOptions parameter. Указывая один из этих параметров, пользователь задает планировщику задач способ планирования задачи в пуле потоков.By specifying one of these options, you tell the task scheduler how to schedule the task on the thread pool. В следующей таблице перечислены различные параметры создания задач.The following table lists the various task creation options.

Значение параметра TaskCreationOptionsTaskCreationOptions parameter value Описание:Description
None Значение по умолчанию, если параметр не задан.The default when no option is specified. Планировщик использует его эвристику по умолчанию для планирования задачи.The scheduler uses its default heuristics to schedule the task.
PreferFairness Указывает, что задачу необходимо планировать так, чтобы созданные раньше задачи выполнялись раньше, а более поздние задачи — позже.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 Указывает, что задача представляет длительную операцию.Specifies that the task represents a long-running operation.
AttachedToParent Указывает, что задача должна быть создана как вложенная дочерняя задача текущей задачи, если таковая существует.Specifies that a task should be created as an attached child of the current task, if one exists. Дополнительные сведения см. в разделе Присоединенные и отсоединенные дочерние задачи.For more information, see Attached and Detached Child Tasks.
DenyChildAttach Указывает, что, если внутренняя задача определяет параметр AttachedToParent, эта задача не станет дочерней присоединенной задачей.Specifies that if an inner task specifies the AttachedToParent option, that task will not become an attached child task.
HideScheduler Указывает, что планировщик задач для задач, созданных вызывающими методами (например, TaskFactory.StartNew или Task<TResult>.ContinueWith) из определенной задачи, — это планировщик по умолчанию, а не планировщик, в котором выполняется эта задача.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.

Параметры можно объединить с использованием побитовой операции ИЛИ.The options may be combined by using a bitwise OR operation. В следующем примере показана задача с параметрами LongRunning и 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()

Задачи, потоки и язык и региональные параметрыTasks, threads, and culture

Каждый поток имеет связанный язык и региональные параметры и язык и региональные параметры пользовательского интерфейса, которые определяются свойствами Thread.CurrentCulture и Thread.CurrentUICulture соответственно.Each thread has an associated culture and UI culture, which is defined by the Thread.CurrentCulture and Thread.CurrentUICulture properties, respectively. Язык и региональные параметры потока используются в таких операциях, как форматирование, анализ, сортировка и сравнение строк.A thread's culture is used in such operations as formatting, parsing, sorting, and string comparison. Язык и региональные параметры пользовательского интерфейса потока используются при поиске ресурсов.A thread's UI culture is used in resource lookup. Как правило, если не заданы язык и региональные параметры по умолчанию для всех потоков в домене приложения с помощью свойств CultureInfo.DefaultThreadCurrentCulture и CultureInfo.DefaultThreadCurrentUICulture, то по умолчанию язык и региональные параметры пользовательского интерфейса потока определяется языком и региональными параметрами системы.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. Если вы явно задаете язык и региональные параметры потока и запускаете новый поток, этот новый поток не наследует язык и региональные параметры вызывающего потока; вместо них по умолчанию используется язык и региональные параметры системы.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. Такого подхода придерживается модель программирования на основе задач для приложений, предназначенных для версий .NET Framework, предшествующих версии .NET Framework 4.6.NET Framework 4.6.The task-based programming model for apps that target versions of the .NET Framework prior to .NET Framework 4.6.NET Framework 4.6 adhere to this practice.

Важно!

Обратите внимание, что язык и региональные параметры вызывающего потока как часть контекста задачи применяются к приложениям, предназначенным.NET Framework 4.6.NET Framework 4.6 для , а не к приложениям, работающим в .NET Framework 4.6.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.NET Framework 4.6, not apps that run under the .NET Framework 4.6.NET Framework 4.6. Вы можете ориентироваться на конкретную версию платформы .NET Framework при создании проекта в Visual Studio, выбрав ее в раскрывающемся списке в верхней части диалогового окна Новый проект, а вне Visual Studio можете использовать атрибут 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. В приложениях, предназначенных для версий .NET Framework до .NET Framework 4.6.NET Framework 4.6 или не предназначенных для определенной версии .NET Framework, язык и региональные параметры задачи по-прежнему определяются языком и региональными параметрами потока, в котором она выполняется.For apps that target versions of the .NET Framework prior to the .NET Framework 4.6.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.

Начиная с приложений, предназначенных для .NET Framework 4.6.NET Framework 4.6, язык и региональные параметры вызывающего потока наследуются каждой задачей, даже если задача выполняется асинхронно в потоке пула потоков.Starting with apps that target the .NET Framework 4.6.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.

Следующий пример иллюстрирует это.The following example provides a simple illustration. Он использует атрибут TargetFrameworkAttribute для указания .NET Framework 4.6.NET Framework 4.6 и изменяет текущий язык и региональные параметры приложения на французский (Франция), а если французский (Франция) уже является текущим языком, то изменяет его на английский (США).It uses the TargetFrameworkAttribute attribute to target the .NET Framework 4.6.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). Затем он вызывает делегат с именем formatDelegate, который возвращает некоторые числа в виде значений валюты в новом языке и региональных параметрах.It then invokes a delegate named formatDelegate that returns some numbers formatted as currency values in the new culture. Обратите внимание, что независимо от того, действует ли делегат как синхронная или асинхронная задача, он возвращает ожидаемый результат, поскольку язык и региональные параметры вызывающего потока наследуются асинхронной задачей.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 €

При использовании Visual Studio можно не указывать атрибут TargetFrameworkAttribute, выбрав вместо этого .NET Framework 4.6 в качестве целевой платформы при создании проекта в диалоговом окне Новый проект.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.

Чтобы получить выходные данные, которые отражают поведение приложений для версий платформы .NET Framework до версии .NET Framework 4.6.NET Framework 4.6, удалите атрибут TargetFrameworkAttribute из исходного кода.For output that reflects the behavior of apps the target versions of the .NET Framework prior to .NET Framework 4.6.NET Framework 4.6, remove the TargetFrameworkAttribute attribute from the source code. Выходные данные будут отражать соглашения о форматировании языка и региональных параметров системы по умолчанию, а не языка и региональных параметров вызывающего потока.The output will reflect the formatting conventions of the default system culture, not the culture of the calling thread.

Дополнительные сведения об асинхронных задачах и языке и региональных параметрах см. в разделе "Язык и региональные параметры и асинхронные операции на основе задач" в описании класса CultureInfo.For more information on asynchronous tasks and culture, see the "Culture and asynchronous task-based operations" section in the CultureInfo topic.

Создание продолжений задачCreating task continuations

С помощью методов Task.ContinueWith и Task<TResult>.ContinueWith можно указать задачу, которую нужно запускать по завершении предшествующей задачи.The Task.ContinueWith and Task<TResult>.ContinueWith methods let you specify a task to start when the antecedent task finishes. Делегат задачи продолжения передается в качестве ссылки на предшествующую задачу, чтобы он мог проверить состояние предшествующей задачи и, получив значение свойства Task<TResult>.Result, использовать выходные данные предшествующей задачи в качестве входных данных для продолжения.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.

В следующем примере задача getData запускается вызовом метода 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. Задача processData запускается автоматически по завершении задачи getData, а задача displayData запускается по завершении задачи processData.The processData task is started automatically when getData finishes, and displayData is started when processData finishes. Задача getData создает целочисленный массив, доступный задаче processData через свойство getData задачи Task<TResult>.Result.getData produces an integer array, which is accessible to the processData task through the getData task's Task<TResult>.Result property. Задача processData обрабатывает этот массив и возвращает результат, тип которого определяется на основе возвращаемого типа лямбда-выражения, переданного методу 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. Задача displayData выполняется автоматически по завершении задачи processData, и объект Tuple<T1,T2,T3>, возвращенный лямбда-выражением processData, доступен задаче displayData через свойство processData задачи 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. Задача displayData принимает результат задачи processData и выдает результат, тип которого определяется аналогичным образом. Этот результат становится доступным программе в свойстве 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

Поскольку метод Task.ContinueWith является экземплярным, вызовы этого метода можно объединять в цепочку, а не создавать экземпляр объекта Task<TResult> для каждой предшествующей задачи.Because Task.ContinueWith is an instance method, you can chain method calls together instead of instantiating a Task<TResult> object for each antecedent task. Следующий пример функционально идентичен предыдущему, за исключением того, вызовы метода Task.ContinueWith в нем объединяются в цепочку.The following example is functionally identical to the previous example, except that it chains together calls to the Task.ContinueWith method. Обратите внимание, что объект Task<TResult>, возвращаемый цепочкой вызовов метода, является последней задачей продолжения.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

С помощью методов ContinueWhenAll и ContinueWhenAny можно продолжить выполнение с нескольких задач.The ContinueWhenAll and ContinueWhenAny methods enable you to continue from multiple tasks.

Подробнее см. в разделе Создание цепочки задач с помощью задач продолжения.For more information, see Chaining Tasks by Using Continuation Tasks.

Создание отсоединенных дочерних задачCreating detached child tasks

Если в пользовательском коде, выполняемом в некоторой задаче, создается новая задача и не задается параметр AttachedToParent, новая задача не синхронизируется с родительской никаким особым способом.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. Такой тип несинхронизированной задачи называется отсоединенной вложенной задачей или отсоединенной дочерней задачей.This type of non-synchronized task is called a detached nested task or detached child task. В следующем примере показана задача, создающая одну отсоединенную дочернюю задачу.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.

Обратите внимание, что родительская задача не ожидает завершения отсоединенной дочерней задачи.Note that the parent task does not wait for the detached child task to finish.

Создание дочерних задачCreating child tasks

Если пользовательский код, который выполняется в задаче, создает новую задачу с параметром AttachedToParent, эта новая задача считается присоединенной дочерней задачей родительской задачи.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. Параметр AttachedToParent можно использовать для выражения структурированного параллелизма задач, поскольку родительская задача неявно ожидает завершения всех присоединенных дочерних задач.You can use the AttachedToParent option to express structured task parallelism, because the parent task implicitly waits for all attached child tasks to finish. В следующем примере показана родительская задача, создающая десять присоединенных дочерних задач.The following example shows a parent task that creates ten attached child tasks. Обратите внимание, что в этом примере вызывается метод Task.Wait для ожидания завершения родительской задачи. Явное ожидание завершения присоединенных дочерних задач не требуется.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.

В родительской задаче может использоваться параметр TaskCreationOptions.DenyChildAttach, не позволяющий другим задачам присоединяться к ней.A parent task can use the TaskCreationOptions.DenyChildAttach option to prevent other tasks from attaching to the parent task. Дополнительные сведения см. в разделе Присоединенные и отсоединенные дочерние задачи.For more information, see Attached and Detached Child Tasks.

Ожидание завершения задачWaiting for tasks to finish

Типы System.Threading.Tasks.Task и System.Threading.Tasks.Task<TResult> предоставляют несколько перегрузок методов Task.Wait и System.Threading.Tasks.Task.Wait, которые позволяют ожидать завершения задачи.The System.Threading.Tasks.Task and System.Threading.Tasks.Task<TResult> types provide several overloads of the Task.Wait and System.Threading.Tasks.Task.Wait methods that enable you to wait for a task to finish. Кроме того, перегрузки статических методов Task.WaitAll и Task.WaitAny позволяют ожидать завершения какого-либо или всех массивов задач.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.

Как правило, ожидание задачи выполняется по одной из следующих причин.Typically, you would wait for a task for one of these reasons:

  • Основной поток зависит от конечного результата, вычисленного задачей.The main thread depends on the final result computed by a task.

  • Необходимо обрабатывать исключения, которые могут быть созданы из задачи.You have to handle exceptions that might be thrown from the task.

  • Приложение может завершиться до окончания выполнения всех задач.The application may terminate before all tasks have completed execution. Например, выполнение консольных приложений завершается после выполнения всего синхронного кода в Main (точке входа приложения).For example, console applications will terminate as soon as all synchronous code in Main (the application entry point) has executed.

В следующем примере показан базовый шаблон, в котором не указана обработка исключений.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...

Пример обработки исключений см. в разделе Обработка исключений.For an example that shows exception handling, see Exception Handling.

Некоторые перегрузки позволяют задать время ожидания, а другие принимают дополнительный объект CancellationToken в качестве входного параметра, чтобы ожидание можно было отменить либо программно, либо в ответ на введенные пользователем данные.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.

При ожидании задачи неявно ожидаются все ее дочерние задачи, созданные с помощью параметра 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 выполняет возврат немедленно, если задача уже завершена.Task.Wait returns immediately if the task has already completed. Любые исключения, вызванные задачей, будут созданы методом Task.Wait, даже если метод Task.Wait был вызван после завершения задачи.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.

Составление задачComposing tasks

Классы Task и Task<TResult> предоставляют несколько методов, позволяющих создать ряд задач для реализации общих шаблонов и более эффективного использования асинхронных возможностей языка, предусмотренных в языках C#, Visual Basic и 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#. В этом подразделе описаны методы WhenAll, WhenAny, Delay и FromResult.This section describes the WhenAll, WhenAny, Delay, and FromResult methods.

Task.WhenAllTask.WhenAll

Метод Task.WhenAll асинхронно ожидает завершения выполнения нескольких объектов Task или Task<TResult>.The Task.WhenAll method asynchronously waits for multiple Task or Task<TResult> objects to finish. Он предоставляет перегруженные версии, позволяющие ожидать неединобразные наборы задач.It provides overloaded versions that enable you to wait for non-uniform sets of tasks. Например, можно ожидать завершения выполнения нескольких объектов Task и Task<TResult> от одного вызова метода.For example, you can wait for multiple Task and Task<TResult> objects to complete from one method call.

Task.WhenAnyTask.WhenAny

Метод Task.WhenAny асинхронно ожидает завершения выполнения одного из нескольких объектов Task или Task<TResult>.The Task.WhenAny method asynchronously waits for one of multiple Task or Task<TResult> objects to finish. Как и метод Task.WhenAll, этот метод предоставляет перегруженные версии, позволяющие ожидать неединобразные наборы задач.As in the Task.WhenAll method, this method provides overloaded versions that enable you to wait for non-uniform sets of tasks. Метод WhenAny особенно полезен в следующих ситуациях.The WhenAny method is especially useful in the following scenarios.

  • Избыточные операции.Redundant operations. Рассмотрим алгоритм или операцию, которые можно выполнить несколькими способами.Consider an algorithm or operation that can be performed in many ways. Метод WhenAny можно использовать для выбора операции, завершающейся первой, и последующей отмены оставшихся операций.You can use the WhenAny method to select the operation that finishes first and then cancel the remaining operations.

  • Операции с чередованием.Interleaved operations. Можно запустить несколько операций, которые все должны завершиться, и использовать метод WhenAny для обработки результатов при завершении каждой операции.You can start multiple operations that must all finish and use the WhenAny method to process results as each operation finishes. После завершения одной операции можно запустить одну или несколько дополнительных задач.After one operation finishes, you can start one or more additional tasks.

  • Регулируемые операции.Throttled operations. Метод WhenAny можно использовать для расширения предыдущего сценария путем ограничения количества одновременно выполняемых операций.You can use the WhenAny method to extend the previous scenario by limiting the number of concurrent operations.

  • Операции с истекшим сроком действия.Expired operations. Метод WhenAny можно использовать, чтобы сделать выбор между одной или несколькими задачами и задачей, завершающейся после определенного времени, например задачей, возвращаемой методом 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. Метод Delay описан в следующем разделе.The Delay method is described in the following section.

Task.DelayTask.Delay

Метод Task.Delay создает объект Task, завершающийся после определенного времени.The Task.Delay method produces a Task object that finishes after the specified time. Этот метод можно использовать для создания циклов, которые иногда запрашивают данные, вводят тайм-ауты, задерживают обработку вводимых пользователем данных на заранее определенное время и т. д.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

С помощью метода Task.FromResult можно создать объект Task<TResult>, содержащий предварительно вычисленный результат.By using the Task.FromResult method, you can create a Task<TResult> object that holds a pre-computed result. Этот метод полезен тогда, когда выполняется асинхронная операция, возвращающая объект Task<TResult>, и результат этого объекта Task<TResult> уже вычислен.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. Пример использования метода FromResult для получения сохраненных в кэше результатов асинхронных операций скачивания можно изучить в статье Практическое руководство. Создание предварительно вычисленных задач.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.

Обработка исключений в задачахHandling exceptions in tasks

Если задача создает одно или несколько исключений, они заключаются в исключение AggregateException.When a task throws one or more exceptions, the exceptions are wrapped in an AggregateException exception. Это исключение распространяется обратно в поток, который соединяется с задачей и обычно является потоком, ожидающим завершения задачи или обращающимся к свойству 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. Такое поведение служит для принудительного выполнения политики .NET Framework, согласно которой все необработанные исключения по умолчанию должны завершать процесс.This behavior serves to enforce the .NET Framework policy that all unhandled exceptions by default should terminate the process. Ниже указаны элементы блока try/catch, с помощью любого из которых вызывающий код может обрабатывать исключения:The calling code can handle the exceptions by using any of the following in a try/catch block:

Присоединяемый поток также может обрабатывать исключения, обращаясь к свойству Exception до того, как задача будет собрана сборщиком мусора.The joining thread can also handle exceptions by accessing the Exception property before the task is garbage-collected. Обращаясь к этому свойству, вы не позволяете необработанному исключению запустить поведение распространения исключений, которое завершает процесс по окончании работы объекта.By accessing this property, you prevent the unhandled exception from triggering the exception propagation behavior that terminates the process when the object is finalized.

Дополнительные сведения об исключениях и задачах см. в разделе Обработка исключений.For more information about exceptions and tasks, see Exception Handling.

Отмена задачCanceling tasks

Класс Task поддерживает совместную отмену и полностью интегрирован с классами System.Threading.CancellationTokenSource и System.Threading.CancellationToken, появившимися в .NET Framework 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. Большинство конструкторов в классе System.Threading.Tasks.Task принимают объект CancellationToken в качестве входного параметра.Many of the constructors in the System.Threading.Tasks.Task class take a CancellationToken object as an input parameter. Многие из перегрузок StartNew и Run также содержат параметр CancellationToken.Many of the StartNew and Run overloads also include a CancellationToken parameter.

Можно создать токен и выдать запрос отмены позднее с помощью класса CancellationTokenSource.You can create the token, and issue the cancellation request at some later time, by using the CancellationTokenSource class. Передайте токен Task в качестве аргумента и ссылайтесь на тот же токен в пользовательском делегате, который не отвечает на запрос отмены.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.

Дополнительные сведения см. в разделах Отмена задач и Практическое руководство. Отмена задачи и ее дочерних элементов.For more information, see Task Cancellation and How to: Cancel a Task and Its Children.

Класс TaskFactoryThe TaskFactory class

Класс TaskFactory предоставляет статические методы, которые инкапсулируют некоторые распространенные шаблоны для создания и запуска задач и задач продолжения.The TaskFactory class provides static methods that encapsulate some common patterns for creating and starting tasks and continuation tasks.

Класс TaskFactory доступен как статическое свойство класса Task или Task<TResult>.The default TaskFactory can be accessed as a static property on the Task class or Task<TResult> class. Кроме того, класс TaskFactory можно создать напрямую и указать различные параметры, включающие CancellationToken, параметр TaskCreationOptions, параметр TaskContinuationOptions или 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. Любые параметры, задаваемые при создании фабрики задач, будут применяться ко всем созданным задачам, если задача Task не создана с помощью перечисления TaskCreationOptions. В этом случае параметры задачи переопределяют параметры фабрики задач.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.

Задачи без делегатовTasks without delegates

В некоторых случаях может потребоваться использовать Task для инкапсуляции некоторой асинхронной операции, которая выполняется внешним компонентом, а не собственным пользовательским делегатом.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. Если операция основана на шаблоне Begin/End модели асинхронного программирования, можно использовать методы FromAsync.If the operation is based on the Asynchronous Programming Model Begin/End pattern, you can use the FromAsync methods. В противном случае можно использовать объект TaskCompletionSource<TResult> для заключения операции в задачу, чтобы получить некоторые преимущества программирования Task, например поддержку распространения исключений и продолжений.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. Дополнительные сведения см. в разделе TaskCompletionSource<TResult>.For more information, see TaskCompletionSource<TResult>.

Пользовательские планировщикиCustom schedulers

Большинство разработчиков приложений или библиотек не обращают внимания на то, на каком процессоре запускается задача, как она синхронизирует свою работу с другими задачами или как она планируется в 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. Им только требуется, чтобы она выполнялась максимально эффективно на главном компьютере.They only require that it execute as efficiently as possible on the host computer. Если требуется более точное управление сведениями планирования, библиотека параллельных задач позволяет настроить некоторые параметры в планировщике заданий по умолчанию и даже предоставить пользовательский планировщик.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. Дополнительные сведения см. в разделе TaskScheduler.For more information, see TaskScheduler.

Библиотека параллельных задач имеет несколько новых открытых типов, которые полезны в параллельных и последовательных сценариях.The TPL has several new public types that are useful in both parallel and sequential scenarios. Они включают несколько потокобезопасных, быстрых и масштабируемых классов коллекций в пространстве имен System.Collections.Concurrent и несколько новых типов синхронизации, например System.Threading.Semaphore и System.Threading.ManualResetEventSlim, которые более эффективны, чем их предшественники для определенных типов рабочих нагрузок.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. Другие новые типы в .NET Framework 4, например System.Threading.Barrier и System.Threading.SpinLock, предоставляют функциональные возможности, которые не были доступны в более ранних выпусках.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. Дополнительные сведения см. в разделе Структуры данных для параллельного программирования.For more information, see Data Structures for Parallel Programming.

Настраиваемые типы задачCustom task types

Наследование из System.Threading.Tasks.Task или System.Threading.Tasks.Task<TResult> не рекомендуется.We recommend that you do not inherit from System.Threading.Tasks.Task or System.Threading.Tasks.Task<TResult>. Вместо этого рекомендуется с помощью свойства AsyncState связать дополнительные данные или состояние с объектом Task или Task<TResult>.Instead, we recommend that you use the AsyncState property to associate additional data or state with a Task or Task<TResult> object. Можно также использовать методы расширения для расширения функциональных возможностей классов Task и Task<TResult>.You can also use extension methods to extend the functionality of the Task and Task<TResult> classes. Дополнительные сведения о методах расширения см. в разделах Методы расширения и Методы расширения.For more information about extension methods, see Extension Methods and Extension Methods.

Если необходимо наследовать от Task или Task<TResult>, классы Run, Run, System.Threading.Tasks.TaskFactory, System.Threading.Tasks.TaskFactory<TResult> и System.Threading.Tasks.TaskCompletionSource<TResult> нельзя использовать для создания экземпляров настраиваемого типа задач, поскольку эти механизмы создают только объекты Task и 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. Кроме того, механизмы продолжения задачи, работу которых обеспечивают Task, Task<TResult>, TaskFactory и TaskFactory<TResult>, нельзя использовать для создания экземпляров настраиваемого типа задач, поскольку эти механизмы также создают только объекты Task и 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.

ЗаголовокTitle Описание:Description
Создание цепочки задач с помощью задач продолженияChaining Tasks by Using Continuation Tasks Описание работы продолжений.Describes how continuations work.
Присоединенные и отсоединенные дочерние задачиAttached and Detached Child Tasks Описание различий между присоединенными и отсоединенными дочерними задачами.Describes the difference between attached and detached child tasks.
Отмена задачTask Cancellation Описание поддержки отмены, встроенной в объект Task.Describes the cancellation support that is built into the Task object.
Обработка исключенийException Handling Описание обработки исключений в параллельных потоках.Describes how exceptions on concurrent threads are handled.
Практическое руководство. Использование функции Parallel_Invoke для выполнения параллельных операцийHow to: Use Parallel.Invoke to Execute Parallel Operations Описание использования Invoke.Describes how to use Invoke.
Практическое руководство. Возвращение значения из задачиHow to: Return a Value from a Task Описание возврата значений из задач.Describes how to return values from tasks.
Практическое руководство. Отмена задачи и ее дочерних элементовHow to: Cancel a Task and Its Children Описание отмены задач.Describes how to cancel tasks.
Практическое руководство. Создание предварительно вычисленных задачHow to: Create Pre-Computed Tasks Описание использования метода Task.FromResult для получения результатов асинхронных операций загрузки, удерживаемых в кэше.Describes how to use the Task.FromResult method to retrieve the results of asynchronous download operations that are held in a cache.
Практическое руководство. Переход по двоичному дереву с помощью параллельных задачHow to: Traverse a Binary Tree with Parallel Tasks Описание использования задач для прохождения двоичного дерева.Describes how to use tasks to traverse a binary tree.
Практическое руководство. Извлечение вложенной задачи из оболочкиHow to: Unwrap a Nested Task Демонстрация использования метода расширения Unwrap.Demonstrates how to use the Unwrap extension method.
Параллелизм данныхData Parallelism Описывает способы использования методов For и ForEach для создания параллельных циклов для данных.Describes how to use For and ForEach to create parallel loops over data.
Параллельное программированиеParallel Programming Узел верхнего уровня для параллельного программирования в .NET Framework.Top level node for .NET Framework parallel programming.

См. такжеSee also