Поделиться через


Отмена задач

Классы System.Threading.Tasks.Task и System.Threading.Tasks.Task<TResult> поддерживают отмену с помощью токенов отмены, новшества .NET Framework 4. Дополнительные сведения см. в разделе Отмена. В классах задач отмена включает взаимодействие между пользовательским делегатом, который представляет операцию отмены, и кодом, который запросил отмену. Успешная отмена включает запрашивающий код, вызывающий метод CancellationTokenSource.Cancel, и пользовательский делегат, своевременно завершающий операцию. Операцию можно завершить одним из следующих способов.

  • Путем простого возврата из делегата. Во многих сценариях этого достаточно, однако экземпляр задачи, отмененный таким образом, переходит в состояние RanToCompletion, а не в состояние Canceled.

  • Путем создания исключения OperationCanceledException и его передачи в токен, на котором была запрошена отмена. Предпочтительным способом сделать это является использование метода ThrowIfCancellationRequested. Задача, отмененная таким образом, переходит в состояние Canceled, которое вызывающий код может использовать для проверки того, что задача ответила на запрос на отмену.

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

Imports System.Threading
Imports System.Threading.Tasks

Module Test
    Sub Main()
        Dim tokenSource2 As New CancellationTokenSource()
        Dim ct As CancellationToken = tokenSource2.Token

        Dim t2 = Task.Factory.StartNew(Sub()
                                           ' Were we already canceled?
                                           ct.ThrowIfCancellationRequested()

                                           Dim moreToDo As Boolean = True
                                           While moreToDo = True
                                               ' Poll on this property if you have to do
                                               ' other cleanup before throwing.
                                               If ct.IsCancellationRequested Then

                                                   ' Clean up here, then...
                                                   ct.ThrowIfCancellationRequested()
                                               End If

                                           End While
                                       End Sub _
        , tokenSource2.Token) ' Pass same token to StartNew.

        ' Cancel the task.
        tokenSource2.Cancel()

        ' Just continue on this thread, or Wait/WaitAll with try-catch:
        Try
            t2.Wait()

        Catch e As AggregateException

            For Each item In e.InnerExceptions
                Console.WriteLine(e.Message & " " & item.Message)
            Next
        End Try

        Console.ReadKey()
    End Sub
End Module
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
    static void Main()
    {

        var tokenSource2 = new CancellationTokenSource();
        CancellationToken ct = tokenSource2.Token;

        var task = Task.Factory.StartNew(() =>
        {

            // Were we already canceled?
            ct.ThrowIfCancellationRequested();

            bool moreToDo = true;
            while (moreToDo)
            {
                // Poll on this property if you have to do
                // other cleanup before throwing.
                if (ct.IsCancellationRequested)
                {
                    // Clean up here, then...
                    ct.ThrowIfCancellationRequested();
                }

            }
        }, tokenSource2.Token); // Pass same token to StartNew.

        tokenSource2.Cancel();

        // Just continue on this thread, or Wait/WaitAll with try-catch:
        try
        {
            task.Wait();
        }
        catch (AggregateException e)
        {
            foreach (var v in e.InnerExceptions)
                Console.WriteLine(e.Message + " " + v.Message);
        }

        Console.ReadKey();
    }
}

Более полный пример содержится в разделе Практическое руководство. Отмена задачи и ее дочерних элементов.

Когда экземпляр задачи обнаруживает исключение OperationCanceledException, созданное пользовательским кодом, он сравнивает токен исключения со связанным токеном (переданным в API-интерфейс, в котором была создана задача). Если они совпадают и свойство IsCancellationRequested токена возвращает значение true, задача интерпретирует это как подтверждение отмены и переходит в состояние Canceled. Если метод Wait или WaitAll не используется для ожидания задачи, она просто устанавливает состояние Canceled.

Если ожидается задача, которая переходит в состояние Canceled, создается и выдается исключение TaskCanceledException (заключенное в исключение AggregateException). Обратите внимание, что это исключение указывает на успешную отмену, а не на сбой. Следовательно, свойство исключения задачи возвращает значение NULL.

Если свойство IsCancellationRequested токена возвращает значение false или если токен исключения не соответствует токену задачи, исключение OperationCanceledException обрабатывается как обычное исключение, вызывая переход задачи в состояние Faulted. Кроме того, обратите внимание, что наличие других исключений также приведет к переходу задачи в состояние Faulted. Состояние завершения задачи можно получить в свойстве Status.

В некоторых случаях задача может продолжить обработку некоторых элементов после запроса отмены.

См. также

Задачи

Практическое руководство. Отмена задачи и ее дочерних элементов

Основные понятия

Отмена