Cancelamento da tarefa

O System.Threading.Tasks.Task e System.Threading.Tasks.Task<TResult> as classes dão suporte ao cancelamento usando tokens de cancelamento. Para saber mais, confira Cancelamento em threads gerenciados. Nas classes de tarefas, o cancelamento envolve a cooperação entre o delegado do usuário, que representa uma operação cancelável, e o código que solicitou o cancelamento. Um cancelamento bem-sucedido envolve o código de solicitação chamar o método CancellationTokenSource.Cancel e o delegado do usuário terminar a operação de forma breve. Você pode terminar a operação ao usar uma destas opções:

  • Retornando do delegado. Em muitos cenários, essa opção é suficiente. No entanto, uma instância de tarefa cancelada dessa forma faz a transição para o TaskStatus.RanToCompletion estado, não para o TaskStatus.Canceled estado.

  • Ao lançar um OperationCanceledException e passá-lo o token no qual o cancelamento foi solicitado. A maneira preferencial de executar é usar o ThrowIfCancellationRequested método. Uma tarefa cancelada dessa forma faz a transição para o estado Cancelado, que o código de chamada pode usar para verificar se a tarefa respondeu à solicitação de cancelamento.

O exemplo a seguir mostra o padrão básico de cancelamento de tarefa que gera a exceção:

Observação

O token é passado para o delegado do usuário e a instância da tarefa.

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

class Program
{
    static async Task Main()
    {
        var tokenSource2 = new CancellationTokenSource();
        CancellationToken ct = tokenSource2.Token;

        var task = Task.Run(() =>
        {
            // 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 Task.Run.

        tokenSource2.Cancel();

        // Just continue on this thread, or await with try-catch:
        try
        {
            await task;
        }
        catch (OperationCanceledException e)
        {
            Console.WriteLine($"{nameof(OperationCanceledException)} thrown with message: {e.Message}");
        }
        finally
        {
            tokenSource2.Dispose();
        }

        Console.ReadKey();
    }
}
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
        Finally
            tokenSource2.Dispose()
        End Try

        Console.ReadKey()
    End Sub
End Module

Para obter um exemplo completo, consulte Como cancelar uma tarefa e seus filhos.

Quando uma instância de tarefa observa uma OperationCanceledException gerada pelo código do usuário, ela compara o token da exceção ao token associado (aquele que foi passado para a API que criou a Tarefa). Se os tokens forem iguais e a propriedade do IsCancellationRequested token retornar true, a tarefa interpretará isso como confirmando o cancelamento e as transições para o estado Cancelado. Se você não usar um Wait método ou WaitAll um método para aguardar a tarefa, a tarefa apenas definirá seu status como Canceled.

Se você estiver aguardando uma Tarefa que faça a transição para o estado Cancelado, uma System.Threading.Tasks.TaskCanceledException exceção (encapsulada em uma exceção AggregateException ) será gerada. Essa exceção indica um cancelamento bem-sucedido em vez de uma situação com falha. Assim, a propriedade Exception da tarefa retorna null.

Se a propriedade do IsCancellationRequested token retornar false ou se o token da exceção não corresponder ao token da Tarefa, ele OperationCanceledException será tratado como uma exceção normal, fazendo com que a Tarefa faça a transição para o estado com falha. A presença de outras exceções também fará com que a Tarefa faça a transição para o estado com falha. Você poderá obter o status da tarefa concluída na propriedade Status.

É possível que uma tarefa possa continuar a processar alguns itens após a solicitação de cancelamento.

Confira também