Класс System.Threading.Tasks.Task

В этой статье приводятся дополнительные замечания к справочной документации по этому API.

Класс Task представляет одну операцию, которая не возвращает значение и обычно выполняется асинхронно. Taskобъекты являются одним из центральных компонентов асинхронного шаблона на основе задач, впервые появившиеся в платформа .NET Framework 4. Так как работа, выполняемая Task объектом, обычно выполняется асинхронно в потоке пула потоков, а не синхронно в основном потоке приложения, можно использовать Status свойство, а также IsCanceledIsCompletedсвойства и IsFaulted свойства для определения состояния задачи. Чаще всего лямбда-выражение используется для указания работы, выполняемой задачей.

Для операций, возвращающих значения, используется Task<TResult> класс.

Создание экземпляра задачи

В следующем примере создаются и выполняются четыре задачи. Три задачи выполняют Action<T> делегат с именем action, который принимает аргумент типа Object. Четвертая задача выполняет лямбда-выражение ( Action делегат), которое определяется встроенным в вызове метода создания задачи. Каждая задача создается и выполняется по-другому:

  • Задача создается путем вызова конструктора класса task t1 , но запускается путем вызова метода Start() только после запуска задачи t2 .

  • Задача t2 создается и запускается в одном вызове метода путем вызова TaskFactory.StartNew(Action<Object>, Object) метода.

  • Задача t3 создается и запускается в одном вызове метода путем вызова Run(Action) метода.

  • Задача t4 выполняется синхронно в основном потоке путем вызова RunSynchronously() метода.

Так как задача t4 выполняется синхронно, она выполняется в основном потоке приложения. Остальные задачи выполняются асинхронно в одном или нескольких потоках пула потоков.

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

class Example1
{
    static void Main()
    {
        Action<object> action = (object obj) =>
                                {
                                    Console.WriteLine("Task={0}, obj={1}, Thread={2}",
                                    Task.CurrentId, obj,
                                    Thread.CurrentThread.ManagedThreadId);
                                };

        // Create a task but do not start it.
        Task t1 = new Task(action, "alpha");

        // Construct a started task
        Task t2 = Task.Factory.StartNew(action, "beta");
        // Block the main thread to demonstrate that t2 is executing
        t2.Wait();

        // Launch t1 
        t1.Start();
        Console.WriteLine("t1 has been launched. (Main Thread={0})",
                          Thread.CurrentThread.ManagedThreadId);
        // Wait for the task to finish.
        t1.Wait();

        // Construct a started task using Task.Run.
        String taskData = "delta";
        Task t3 = Task.Run(() =>
        {
            Console.WriteLine("Task={0}, obj={1}, Thread={2}",
                                                     Task.CurrentId, taskData,
                                                      Thread.CurrentThread.ManagedThreadId);
        });
        // Wait for the task to finish.
        t3.Wait();

        // Construct an unstarted task
        Task t4 = new Task(action, "gamma");
        // Run it synchronously
        t4.RunSynchronously();
        // Although the task was run synchronously, it is a good practice
        // to wait for it in the event exceptions were thrown by the task.
        t4.Wait();
    }
}
// The example displays output like the following:
//       Task=1, obj=beta, Thread=3
//       t1 has been launched. (Main Thread=1)
//       Task=2, obj=alpha, Thread=4
//       Task=3, obj=delta, Thread=3
//       Task=4, obj=gamma, Thread=1
open System.Threading
open System.Threading.Tasks

let action =
    fun (obj: obj) -> printfn $"Task={Task.CurrentId}, obj={obj}, Thread={Thread.CurrentThread.ManagedThreadId}"

// Create a task but do not start it.
let t1 = new Task(action, "alpha")

// Construct a started task
let t2 = Task.Factory.StartNew(action, "beta")
// Block the main thread to demonstrate that t2 is executing
t2.Wait()

// Launch t1
t1.Start()
printfn $"t1 has been launched. (Main Thread={Thread.CurrentThread.ManagedThreadId})"
// Wait for the task to finish.
t1.Wait()

// Construct a started task using Task.Run.
let taskData = "delta"

let t3 =
    Task.Run(fun () -> printfn $"Task={Task.CurrentId}, obj={taskData}, Thread={Thread.CurrentThread.ManagedThreadId}")
// Wait for the task to finish.
t3.Wait()

// Construct an unstarted task
let t4 = new Task(action, "gamma")
// Run it synchronously
t4.RunSynchronously()
// Although the task was run synchronously, it is a good practice
// to wait for it in the event exceptions were thrown by the task.
t4.Wait()


// The example displays output like the following:
//       Task=1, obj=beta, Thread=3
//       t1 has been launched. (Main Thread=1)
//       Task=2, obj=alpha, Thread=4
//       Task=3, obj=delta, Thread=3
//       Task=4, obj=gamma, Thread=1
Imports System.Threading
Imports System.Threading.Tasks

Module Example2
    Public Sub Main()
        Dim action As Action(Of Object) =
              Sub(obj As Object)
                  Console.WriteLine("Task={0}, obj={1}, Thread={2}",
                 Task.CurrentId, obj,
                 Thread.CurrentThread.ManagedThreadId)
              End Sub

        ' Construct an unstarted task
        Dim t1 As New Task(action, "alpha")

        ' Construct a started task
        Dim t2 As Task = Task.Factory.StartNew(action, "beta")
        ' Block the main thread to demonstrate that t2 is executing
        t2.Wait()

        ' Launch t1 
        t1.Start()
        Console.WriteLine("t1 has been launched. (Main Thread={0})",
                          Thread.CurrentThread.ManagedThreadId)
        ' Wait for the task to finish.
        t1.Wait()

        ' Construct a started task using Task.Run.
        Dim taskData As String = "delta"
        Dim t3 As Task = Task.Run(Sub()
                                      Console.WriteLine("Task={0}, obj={1}, Thread={2}",
                                     Task.CurrentId, taskData,
                                     Thread.CurrentThread.ManagedThreadId)
                                  End Sub)
        ' Wait for the task to finish.
        t3.Wait()

        ' Construct an unstarted task
        Dim t4 As New Task(action, "gamma")
        ' Run it synchronously
        t4.RunSynchronously()
        ' Although the task was run synchronously, it is a good practice
        ' to wait for it in the event exceptions were thrown by the task.
        t4.Wait()
    End Sub
End Module
' The example displays output like the following:
'       Task=1, obj=beta, Thread=3
'       t1 has been launched. (Main Thread=1)
'       Task=2, obj=alpha, Thread=3
'       Task=3, obj=delta, Thread=3
'       Task=4, obj=gamma, Thread=1

Создание и выполнение задачи

Экземпляры можно создавать Task различными способами. Наиболее распространенный подход заключается в вызове статического Run метода. Этот Run метод предоставляет простой способ запуска задачи с использованием значений по умолчанию и без необходимости использования дополнительных параметров. В следующем примере метод используется Run(Action) для запуска задачи, которая циклирует, а затем отображается количество итераций цикла:

using System;
using System.Threading.Tasks;

public class Example
{
   public static async Task Main()
   {
      await Task.Run( () => {
                                  // Just loop.
                                  int ctr = 0;
                                  for (ctr = 0; ctr <= 1000000; ctr++)
                                  {}
                                  Console.WriteLine("Finished {0} loop iterations",
                                                    ctr);
                               } );
   }
}
// The example displays the following output:
//        Finished 1000001 loop iterations
open System.Threading.Tasks

let main =
    task {
        do!
            Task.Run(fun () ->
                for i = 0 to 1000000 do
                    printfn $"Finished {i} loop iterations")
    }

main.Wait()

// The example displays the following output:
//        Finished 1000001 loop iterations
Imports System.Threading.Tasks

Module Example1
    Public Sub Main()
        Dim t As Task = Task.Run(Sub()
                                     ' Just loop.
                                     Dim ctr As Integer = 0
                                     For ctr = 0 To 1000000
                                     Next
                                     Console.WriteLine("Finished {0} loop iterations",
                                                    ctr)
                                 End Sub)
        t.Wait()
    End Sub
End Module
' The example displays the following output:
'       Finished 1000001 loop iterations

Альтернативой является статический TaskFactory.StartNew метод. Свойство Task.Factory возвращает TaskFactory объект. Перегрузки TaskFactory.StartNew метода позволяют указать параметры для передачи параметрам создания задачи и планировщику задач. В следующем примере метод используется TaskFactory.StartNew для запуска задачи. Он функционально эквивалентен коду в предыдущем примере.

using System;
using System.Threading.Tasks;

public class Example2
{
    public static void Main()
    {
        Task t = Task.Factory.StartNew(() =>
        {
            // Just loop.
            int ctr = 0;
            for (ctr = 0; ctr <= 1000000; ctr++)
            { }
            Console.WriteLine("Finished {0} loop iterations",
                              ctr);
        });
        t.Wait();
    }
}
// The example displays the following output:
//        Finished 1000001 loop iterations
open System.Threading.Tasks

let t =
    Task.Factory.StartNew(fun () ->
        // Just loop.
        for i = 0 to 1000000 do
            printfn $"Finished {i} loop iterations")

t.Wait()


// The example displays the following output:
//        Finished 1000001 loop iterations
Imports System.Threading.Tasks

Module Example3
    Public Sub Main()
        Dim t As Task = Task.Factory.StartNew(Sub()
                                                  ' Just loop.
                                                  Dim ctr As Integer = 0
                                                  For ctr = 0 To 1000000
                                                  Next
                                                  Console.WriteLine("Finished {0} loop iterations",
                                                    ctr)
                                              End Sub)
        t.Wait()
    End Sub
End Module
' The example displays the following output:
'       Finished 1000001 loop iterations

Дополнительные полные примеры см. в статье "Асинхронное программирование на основе задач".

Отдельное создание и выполнение задачи

Класс Task также предоставляет конструкторы, которые инициализируют задачу, но не планируют ее выполнение. По соображениям Task.Run производительности метод или TaskFactory.StartNew метод является предпочтительным механизмом для создания и планирования вычислительных задач, но для сценариев, когда создание и планирование должны быть разделены, можно использовать конструкторы, а затем вызвать Task.Start метод для планирования задачи для выполнения в дальнейшем.

Дождитесь завершения задачи

Так как задачи обычно выполняются асинхронно в потоке пула потоков, поток, который создает и запускает задачу, продолжает выполнение сразу после создания экземпляра задачи. В некоторых случаях, когда вызывающий поток является основным потоком приложения, приложение может завершиться до начала выполнения задачи. В других случаях логика приложения может требовать продолжения выполнения вызывающего потока только в том случае, если одна или несколько задач завершили выполнение. Вы можете синхронизировать выполнение вызывающего потока и асинхронные задачи, которые он запускает, вызвав Wait метод, чтобы дождаться завершения одной или нескольких задач.

Чтобы дождаться завершения одной задачи, можно вызвать его Task.Wait метод. Вызов Wait метода блокирует вызывающий поток до завершения выполнения одного экземпляра класса.

В следующем примере метод без Wait() параметров вызывает безусловную ожидание завершения задачи. Задача имитирует работу, вызывая Thread.Sleep метод спящего режима в течение двух секунд.

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

class Program
{
    static Random rand = new Random();

    static void Main()
    {
        // Wait on a single task with no timeout specified.
        Task taskA = Task.Run( () => Thread.Sleep(2000));
        Console.WriteLine("taskA Status: {0}", taskA.Status);
        try {
          taskA.Wait();
          Console.WriteLine("taskA Status: {0}", taskA.Status);
       } 
       catch (AggregateException) {
          Console.WriteLine("Exception in taskA.");
       }   
    }    
}
// The example displays output like the following:
//     taskA Status: WaitingToRun
//     taskA Status: RanToCompletion
open System
open System.Threading
open System.Threading.Tasks

let rand = Random()

// Wait on a single task with no timeout specified.
let taskA = Task.Run(fun () -> Thread.Sleep 2000)
printfn $"taskA Status: {taskA.Status}"
try 
    taskA.Wait()
    printfn $"taskA Status: {taskA.Status}"

with :? AggregateException ->
    printfn "Exception in taskA."

// The example displays output like the following:
//     taskA Status: WaitingToRun
//     taskA Status: RanToCompletion
Imports System.Threading
Imports System.Threading.Tasks

Module Example4
    Public Sub Main()
        ' Wait on a single task with no timeout specified.
        Dim taskA = Task.Run(Sub() Thread.Sleep(2000))
        Console.WriteLine("taskA Status: {0}", taskA.Status)
        Try
            taskA.Wait()
            Console.WriteLine("taskA Status: {0}", taskA.Status)
        Catch e As AggregateException
            Console.WriteLine("Exception in taskA.")
        End Try
    End Sub
End Module
' The example displays output like the following:
'     taskA Status: WaitingToRun
'     taskA Status: RanToCompletion

Вы также можете условно ждать завершения задачи. Wait(TimeSpan) Методы Wait(Int32) блокируют вызывающий поток до тех пор, пока задача не завершится или истекает интервал времени ожидания. Так как в следующем примере запускается задача, которая спит в течение двух секунд, но определяет значение времени ожидания одно секунды, вызывающие блоки потоков до истечения срока ожидания и до завершения выполнения задачи.

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

public class Example5
{
    public static void Main()
    {
        // Wait on a single task with a timeout specified.
        Task taskA = Task.Run(() => Thread.Sleep(2000));
        try
        {
            taskA.Wait(1000);       // Wait for 1 second.
            bool completed = taskA.IsCompleted;
            Console.WriteLine("Task A completed: {0}, Status: {1}",
                             completed, taskA.Status);
            if (!completed)
                Console.WriteLine("Timed out before task A completed.");
        }
        catch (AggregateException)
        {
            Console.WriteLine("Exception in taskA.");
        }
    }
}
// The example displays output like the following:
//     Task A completed: False, Status: Running
//     Timed out before task A completed.
open System
open System.Threading
open System.Threading.Tasks

// Wait on a single task with a timeout specified.
let taskA = Task.Run(fun () -> Thread.Sleep 2000)

try
    taskA.Wait 1000 |> ignore // Wait for 1 second.
    let completed = taskA.IsCompleted
    printfn $"Task A completed: {completed}, Status: {taskA.Status}"

    if not completed then
        printfn "Timed out before task A completed."

with :? AggregateException ->
    printfn "Exception in taskA."


// The example displays output like the following:
//     Task A completed: False, Status: Running
//     Timed out before task A completed.
Imports System.Threading
Imports System.Threading.Tasks

Module Example5
    Public Sub Main()
        ' Wait on a single task with a timeout specified.
        Dim taskA As Task = Task.Run(Sub() Thread.Sleep(2000))
        Try
            taskA.Wait(1000)        ' Wait for 1 second.
            Dim completed As Boolean = taskA.IsCompleted
            Console.WriteLine("Task.Completed: {0}, Status: {1}",
                           completed, taskA.Status)
            If Not completed Then
                Console.WriteLine("Timed out before task A completed.")
            End If
        Catch e As AggregateException
            Console.WriteLine("Exception in taskA.")
        End Try
    End Sub
End Module
' The example displays the following output:
'     Task A completed: False, Status: Running
'     Timed out before task A completed.

Вы также можете предоставить маркер отмены, вызвав Wait(CancellationToken) методы и Wait(Int32, CancellationToken) методы. Если свойство маркера IsCancellationRequested находится true или становится true во время Wait выполнения метода, метод вызывает OperationCanceledExceptionисключение.

В некоторых случаях может потребоваться дождаться завершения первой серии выполняемых задач, но не волнуйтесь, какая задача это. Для этого можно вызвать одну из перегрузок Task.WaitAny метода. В следующем примере создаются три задачи, каждая из которых спит для интервала, определяемого генератором случайных чисел. Метод WaitAny(Task[]) ожидает завершения первой задачи. Затем в примере отображаются сведения о состоянии всех трех задач.

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

public class Example4
{
    public static void Main()
    {
        var tasks = new Task[3];
        var rnd = new Random();
        for (int ctr = 0; ctr <= 2; ctr++)
            tasks[ctr] = Task.Run(() => Thread.Sleep(rnd.Next(500, 3000)));

        try
        {
            int index = Task.WaitAny(tasks);
            Console.WriteLine("Task #{0} completed first.\n", tasks[index].Id);
            Console.WriteLine("Status of all tasks:");
            foreach (var t in tasks)
                Console.WriteLine("   Task #{0}: {1}", t.Id, t.Status);
        }
        catch (AggregateException)
        {
            Console.WriteLine("An exception occurred.");
        }
    }
}
// The example displays output like the following:
//     Task #1 completed first.
//     
//     Status of all tasks:
//        Task #3: Running
//        Task #1: RanToCompletion
//        Task #4: Running
open System
open System.Threading
open System.Threading.Tasks

let rnd = new Random()

let tasks =
    [| for _ = 0 to 2 do
           Task.Run(fun () -> rnd.Next(500, 3000) |> Thread.Sleep) |]

try
    let index = Task.WaitAny tasks
    printfn $"Task #{tasks[index].Id} completed first.\n"
    printfn "Status of all tasks:"

    for t in tasks do
        printfn $"   Task #{t.Id}: {t.Status}"

with :? AggregateException ->
    printfn "An exception occurred."

// The example displays output like the following:
//     Task #1 completed first.
//
//     Status of all tasks:
//        Task #3: Running
//        Task #1: RanToCompletion
//        Task #4: Running
Imports System.Threading

Module Example8
    Public Sub Main()
        Dim tasks(2) As Task
        Dim rnd As New Random()
        For ctr As Integer = 0 To 2
            tasks(ctr) = Task.Run(Sub() Thread.Sleep(rnd.Next(500, 3000)))
        Next

        Try
            Dim index As Integer = Task.WaitAny(tasks)
            Console.WriteLine("Task #{0} completed first.", tasks(index).Id)
            Console.WriteLine()
            Console.WriteLine("Status of all tasks:")
            For Each t In tasks
                Console.WriteLine("   Task #{0}: {1}", t.Id, t.Status)
            Next
        Catch e As AggregateException
            Console.WriteLine("An exception occurred.")
        End Try
    End Sub
End Module
' The example displays output like the following:
'     Task #1 completed first.
'     
'     Status of all tasks:
'        Task #3: Running
'        Task #1: RanToCompletion
'        Task #4: Running

Вы также можете дождаться завершения всей серии задач, вызвав WaitAll метод. В следующем примере создаются десять задач, ожидает завершения всех десяти задач, а затем отображается их состояние.

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

public class Example3
{
    public static void Main()
    {
        // Wait for all tasks to complete.
        Task[] tasks = new Task[10];
        for (int i = 0; i < 10; i++)
        {
            tasks[i] = Task.Run(() => Thread.Sleep(2000));
        }
        try
        {
            Task.WaitAll(tasks);
        }
        catch (AggregateException ae)
        {
            Console.WriteLine("One or more exceptions occurred: ");
            foreach (var ex in ae.Flatten().InnerExceptions)
                Console.WriteLine("   {0}", ex.Message);
        }

        Console.WriteLine("Status of completed tasks:");
        foreach (var t in tasks)
            Console.WriteLine("   Task #{0}: {1}", t.Id, t.Status);
    }
}
// The example displays the following output:
//     Status of completed tasks:
//        Task #2: RanToCompletion
//        Task #1: RanToCompletion
//        Task #3: RanToCompletion
//        Task #4: RanToCompletion
//        Task #6: RanToCompletion
//        Task #5: RanToCompletion
//        Task #7: RanToCompletion
//        Task #8: RanToCompletion
//        Task #9: RanToCompletion
//        Task #10: RanToCompletion
open System
open System.Threading
open System.Threading.Tasks

// Wait for all tasks to complete.
let tasks =
    [| for _ = 0 to 9 do
           Task.Run(fun () -> Thread.Sleep 2000) |]

try
    Task.WaitAll tasks

with :? AggregateException as ae ->
    printfn "One or more exceptions occurred: "

    for ex in ae.Flatten().InnerExceptions do
        printfn $"   {ex.Message}"

printfn "Status of completed tasks:"

for t in tasks do
    printfn $"   Task #{t.Id}: {t.Status}"

// The example displays the following output:
//     Status of completed tasks:
//        Task #2: RanToCompletion
//        Task #1: RanToCompletion
//        Task #3: RanToCompletion
//        Task #4: RanToCompletion
//        Task #6: RanToCompletion
//        Task #5: RanToCompletion
//        Task #7: RanToCompletion
//        Task #8: RanToCompletion
//        Task #9: RanToCompletion
//        Task #10: RanToCompletion
Imports System.Threading
Imports System.Threading.Tasks

Module Example6
    Public Sub Main()
        ' Wait for all tasks to complete.
        Dim tasks(9) As Task
        For i As Integer = 0 To 9
            tasks(i) = Task.Run(Sub() Thread.Sleep(2000))
        Next
        Try
            Task.WaitAll(tasks)
        Catch ae As AggregateException
            Console.WriteLine("One or more exceptions occurred: ")
            For Each ex In ae.Flatten().InnerExceptions
                Console.WriteLine("   {0}", ex.Message)
            Next
        End Try

        Console.WriteLine("Status of completed tasks:")
        For Each t In tasks
            Console.WriteLine("   Task #{0}: {1}", t.Id, t.Status)
        Next
    End Sub
End Module
' The example displays the following output:
'     Status of completed tasks:
'        Task #2: RanToCompletion
'        Task #1: RanToCompletion
'        Task #3: RanToCompletion
'        Task #4: RanToCompletion
'        Task #6: RanToCompletion
'        Task #5: RanToCompletion
'        Task #7: RanToCompletion
'        Task #8: RanToCompletion
'        Task #9: RanToCompletion
'        Task #10: RanToCompletion

Обратите внимание, что при ожидании завершения одной или нескольких задач все исключения, создаваемые в выполняемых задачах, распространяются в потоке, который вызывает Wait метод, как показано в следующем примере. Он запускает 12 задач, три из которых выполняются обычно и три из которых вызывают исключение. Из оставшихся шести задач три отменяются до их запуска, а три отменяются при выполнении. Исключения создаются в вызове WaitAll метода и обрабатываются блоком/trycatch.

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

public class Example6
{
    public static void Main()
    {
        // Create a cancellation token and cancel it.
        var source1 = new CancellationTokenSource();
        var token1 = source1.Token;
        source1.Cancel();
        // Create a cancellation token for later cancellation.
        var source2 = new CancellationTokenSource();
        var token2 = source2.Token;

        // Create a series of tasks that will complete, be cancelled, 
        // timeout, or throw an exception.
        Task[] tasks = new Task[12];
        for (int i = 0; i < 12; i++)
        {
            switch (i % 4)
            {
                // Task should run to completion.
                case 0:
                    tasks[i] = Task.Run(() => Thread.Sleep(2000));
                    break;
                // Task should be set to canceled state.
                case 1:
                    tasks[i] = Task.Run(() => Thread.Sleep(2000),
                             token1);
                    break;
                case 2:
                    // Task should throw an exception.
                    tasks[i] = Task.Run(() => { throw new NotSupportedException(); });
                    break;
                case 3:
                    // Task should examine cancellation token.
                    tasks[i] = Task.Run(() =>
                    {
                        Thread.Sleep(2000);
                        if (token2.IsCancellationRequested)
                            token2.ThrowIfCancellationRequested();
                        Thread.Sleep(500);
                    }, token2);
                    break;
            }
        }
        Thread.Sleep(250);
        source2.Cancel();

        try
        {
            Task.WaitAll(tasks);
        }
        catch (AggregateException ae)
        {
            Console.WriteLine("One or more exceptions occurred:");
            foreach (var ex in ae.InnerExceptions)
                Console.WriteLine("   {0}: {1}", ex.GetType().Name, ex.Message);
        }

        Console.WriteLine("\nStatus of tasks:");
        foreach (var t in tasks)
        {
            Console.WriteLine("   Task #{0}: {1}", t.Id, t.Status);
            if (t.Exception != null)
            {
                foreach (var ex in t.Exception.InnerExceptions)
                    Console.WriteLine("      {0}: {1}", ex.GetType().Name,
                                      ex.Message);
            }
        }
    }
}
// The example displays output like the following:
//   One or more exceptions occurred:
//      TaskCanceledException: A task was canceled.
//      NotSupportedException: Specified method is not supported.
//      TaskCanceledException: A task was canceled.
//      TaskCanceledException: A task was canceled.
//      NotSupportedException: Specified method is not supported.
//      TaskCanceledException: A task was canceled.
//      TaskCanceledException: A task was canceled.
//      NotSupportedException: Specified method is not supported.
//      TaskCanceledException: A task was canceled.
//   
//   Status of tasks:
//      Task #13: RanToCompletion
//      Task #1: Canceled
//      Task #3: Faulted
//         NotSupportedException: Specified method is not supported.
//      Task #8: Canceled
//      Task #14: RanToCompletion
//      Task #4: Canceled
//      Task #6: Faulted
//         NotSupportedException: Specified method is not supported.
//      Task #7: Canceled
//      Task #15: RanToCompletion
//      Task #9: Canceled
//      Task #11: Faulted
//         NotSupportedException: Specified method is not supported.
//      Task #12: Canceled
open System
open System.Threading
open System.Threading.Tasks

// Create a cancellation token and cancel it.
let source1 = new CancellationTokenSource()
let token1 = source1.Token
source1.Cancel()
// Create a cancellation token for later cancellation.
let source2 = new CancellationTokenSource()
let token2 = source2.Token

// Create a series of tasks that will complete, be cancelled,
// timeout, or throw an exception.
let tasks =
    [| for i in 0..11 do
           match i % 4 with
           // Task should run to completion.
           | 0 -> Task.Run(fun () -> Thread.Sleep 2000)
           // Task should be set to canceled state.
           | 1 -> Task.Run(fun () -> Thread.Sleep 2000, token1)
           // Task should throw an exception.
           | 2 -> Task.Run(fun () -> NotSupportedException())
           // Task should examine cancellation token.
           | _ ->
               Task.Run(fun () ->
                   Thread.Sleep 2000

                   if token2.IsCancellationRequested then
                       token2.ThrowIfCancellationRequested()

                   Thread.Sleep 500, token2) |]


Thread.Sleep 250
source2.Cancel()

try
    Task.WaitAll tasks

with :? AggregateException as ae ->
    printfn "One or more exceptions occurred:"

    for ex in ae.InnerExceptions do
        printfn $"   {ex.GetType().Name}: {ex.Message}"

printfn "\nStatus of tasks:"

for t in tasks do
    printfn $"   Task #{t.Id}: {t.Status}"

    if isNull t.Exception |> not then
        for ex in t.Exception.InnerExceptions do
            printfn $"      {ex.GetType().Name}: {ex.Message}"

// The example displays output like the following:
//   One or more exceptions occurred:
//      TaskCanceledException: A task was canceled.
//      NotSupportedException: Specified method is not supported.
//      TaskCanceledException: A task was canceled.
//      TaskCanceledException: A task was canceled.
//      NotSupportedException: Specified method is not supported.
//      TaskCanceledException: A task was canceled.
//      TaskCanceledException: A task was canceled.
//      NotSupportedException: Specified method is not supported.
//      TaskCanceledException: A task was canceled.
//
//   Status of tasks:
//      Task #13: RanToCompletion
//      Task #1: Canceled
//      Task #3: Faulted
//         NotSupportedException: Specified method is not supported.
//      Task #8: Canceled
//      Task #14: RanToCompletion
//      Task #4: Canceled
//      Task #6: Faulted
//         NotSupportedException: Specified method is not supported.
//      Task #7: Canceled
//      Task #15: RanToCompletion
//      Task #9: Canceled
//      Task #11: Faulted
//         NotSupportedException: Specified method is not supported.
//      Task #12: Canceled
Imports System.Threading
Imports System.Threading.Tasks

Module Example7
    Public Sub Main()
        ' Create a cancellation token and cancel it.
        Dim source1 As New CancellationTokenSource()
        Dim token1 As CancellationToken = source1.Token
        source1.Cancel()
        ' Create a cancellation token for later cancellation.
        Dim source2 As New CancellationTokenSource()
        Dim token2 As CancellationToken = source2.Token

        ' Create a series of tasks that will complete, be cancelled, 
        ' timeout, or throw an exception.
        Dim tasks(11) As Task
        For i As Integer = 0 To 11
            Select Case i Mod 4
             ' Task should run to completion.
                Case 0
                    tasks(i) = Task.Run(Sub() Thread.Sleep(2000))
             ' Task should be set to canceled state.
                Case 1
                    tasks(i) = Task.Run(Sub() Thread.Sleep(2000), token1)
                Case 2
                    ' Task should throw an exception.
                    tasks(i) = Task.Run(Sub()
                                            Throw New NotSupportedException()
                                        End Sub)
                Case 3
                    ' Task should examine cancellation token.
                    tasks(i) = Task.Run(Sub()
                                            Thread.Sleep(2000)
                                            If token2.IsCancellationRequested Then
                                                token2.ThrowIfCancellationRequested()
                                            End If
                                            Thread.Sleep(500)
                                        End Sub, token2)
            End Select
        Next
        Thread.Sleep(250)
        source2.Cancel()

        Try
            Task.WaitAll(tasks)
        Catch ae As AggregateException
            Console.WriteLine("One or more exceptions occurred:")
            For Each ex In ae.InnerExceptions
                Console.WriteLine("   {0}: {1}", ex.GetType().Name, ex.Message)
            Next
        End Try
        Console.WriteLine()

        Console.WriteLine("Status of tasks:")
        For Each t In tasks
            Console.WriteLine("   Task #{0}: {1}", t.Id, t.Status)
            If t.Exception IsNot Nothing Then
                For Each ex In t.Exception.InnerExceptions
                    Console.WriteLine("      {0}: {1}", ex.GetType().Name,
                                 ex.Message)
                Next
            End If
        Next
    End Sub
End Module
' The example displays output like the following:
'   One or more exceptions occurred:
'      TaskCanceledException: A task was canceled.
'      NotSupportedException: Specified method is not supported.
'      TaskCanceledException: A task was canceled.
'      TaskCanceledException: A task was canceled.
'      NotSupportedException: Specified method is not supported.
'      TaskCanceledException: A task was canceled.
'      TaskCanceledException: A task was canceled.
'      NotSupportedException: Specified method is not supported.
'      TaskCanceledException: A task was canceled.
'   
'   Status of tasks:
'      Task #13: RanToCompletion
'      Task #1: Canceled
'      Task #3: Faulted
'         NotSupportedException: Specified method is not supported.
'      Task #8: Canceled
'      Task #14: RanToCompletion
'      Task #4: Canceled
'      Task #6: Faulted
'         NotSupportedException: Specified method is not supported.
'      Task #7: Canceled
'      Task #15: RanToCompletion
'      Task #9: Canceled
'      Task #11: Faulted
'         NotSupportedException: Specified method is not supported.
'      Task #12: Canceled

Дополнительные сведения об обработке исключений в асинхронных операциях на основе задач см. в разделе "Обработка исключений".

Задачи и региональные параметры

Начиная с классических приложений, предназначенных для платформа .NET Framework 4.6, язык и региональные параметры потока, создающего и вызывающего задачу, становятся частью контекста потока. То есть независимо от текущего языка и региональных параметров потока, на котором выполняется задача, текущий язык и региональные параметры задачи — это язык и региональные параметры вызывающего потока. Для приложений, предназначенных для версий платформа .NET Framework до платформа .NET Framework 4.6, язык и региональные параметры задачи — это язык и региональные параметры потока, на котором выполняется задача. Дополнительные сведения см. в CultureInfo разделе "Язык и асинхронные операции на основе задач".

Примечание.

Приложения Магазина следуют среда выполнения Windows в настройке и получении языка и региональных параметров по умолчанию.

Для разработчиков отладчика

Для разработчиков, реализующих пользовательские отладчики, может быть полезно несколько внутренних и частных членов задачи (они могут измениться с выпуска на выпуск). Поле m_taskId служит резервным хранилищем для Id свойства, однако доступ к этому полю непосредственно из отладчика может быть более эффективным, чем доступ к тому же значению через метод получения свойства ( s_taskIdCounter счетчик используется для получения следующего доступного идентификатора для задачи). Аналогичным образом m_stateFlags поле хранит сведения о текущем этапе жизненного цикла задачи, сведения также доступны через Status свойство. Поле m_action сохраняет ссылку на делегат задачи, а m_stateObject поле сохраняет асинхронное состояние, переданное разработчику. Наконец, для отладчиков, которые анализируют кадры стека, InternalWait метод служит потенциальным маркером при вводе задачи операции ожидания.