Cancelación de tareas

Las clases System.Threading.Tasks.Task<TResult> y System.Threading.Tasks.Task admiten la cancelación a través del uso de tokens de cancelación, que son una novedad de .NET Framework 4. Para obtener más información, vea Cancelación. En las clases de tareas, la cancelación implica la cooperación entre el delegado de usuario, que representa una operación cancelable y el código que solicitó la cancelación. Una cancelación correcta implica que el código solicitante llame al método CancellationTokenSource.Cancel y que el delegado de usuario termine la operación en el tiempo esperado. Puede finalizar la operación a través de una de estas opciones:

  • Devolver simplemente un valor del delegado. En muchos escenarios esto es suficiente; sin embargo, una instancia de tarea "cancelada" de esta manera cambia al estado RanToCompletion, no al estado Canceled.

  • Producir una excepción OperationCanceledException y pasarle el token en el que se solicitó la cancelación. En este caso, se prefiere usar el método ThrowIfCancellationRequested. Una tarea cancelada de esta manera cambia al estado Canceled, que sirve al código que realiza la llamada para comprobar que la tarea respondió a su solicitud de cancelación.

En el siguiente ejemplo se muestra el modelo básico para la opción de cancelación de tareas que produce la excepción. Observe que el token se pasa al delegado de usuario y a la propia instancia de la tarea.

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();
    }
}

Para obtener un ejemplo más completo, vea Cómo: Cancelar una tarea y sus elementos secundarios.

Cuando una instancia de tarea observa una excepción OperationCanceledException iniciada desde el código de usuario, compara el token de la excepción con su token asociado (el que se pasó a la API que creó la tarea). Si son iguales y la propiedad IsCancellationRequested del token devuelve true, la tarea lo interpreta como una confirmación de cancelación y pasa al estado Canceled. Si no se usa un método WaitAll o Wait para esperar a la tarea, esta simplemente establece su estado en Canceled.

Si espera en una tarea que cambia al estado Canceled, se crea y se inicia una excepción TaskCanceledException (encapsulada en AggregateException). Observe que esta excepción indica la cancelación correcta en lugar de una situación de error. Por consiguiente, la propiedad Exception de la tarea devuelve Null.

Si la propiedad IsCancellationRequested del token devuelve False o si el token de la excepción no coincide con el token de la tarea, OperationCanceledException se trata como una excepción normal, por lo que la tarea cambia al estado Faulted. Observe también que la presencia de otras excepciones también hará que la tarea pase al estado Faulted. Puede obtener el estado de la tarea completada en la propiedad Status.

Es posible que una tarea continúe procesando algunos elementos una vez solicitada la cancelación.

Vea también

Tareas

Cómo: Cancelar una tarea y sus elementos secundarios

Conceptos

Cancelación