Compartir a través de


Cancelación de tareas

Las System.Threading.Tasks.Task clases y System.Threading.Tasks.Task<TResult> admiten la cancelación mediante tokens de cancelación. Para más información, consulte el tema sobre la cancelación en subprocesos administrados. En las clases Task, 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 CancellationTokenSource.Cancel método y el delegado del usuario termine la operación de forma oportuna. Puede finalizar la operación mediante una de estas opciones:

  • Devolver un valor del delegado. En muchos escenarios, esta opción es suficiente. Sin embargo, una instancia de tarea que se cancela de esta manera pasa al estado TaskStatus.RanToCompletion, no al estado TaskStatus.Canceled.

  • Producir una excepción OperationCanceledException y pasarle el token en el que se solicitó la cancelación. La manera preferida de llevar a cabo es usar el método ThrowIfCancellationRequested. Una tarea que se cancela de esta manera realiza la transición al estado Cancelado, que el código de llamada puede usar para comprobar que la tarea respondió a su solicitud de cancelación.

En el ejemplo siguiente se muestra el patrón básico para la cancelación de tareas que produce la excepción:

Nota:

El token se pasa al delegado de usuario y a la instancia de tarea.

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 consultar un ejemplo más completo, vea Procedimiento para cancelar una tarea y sus elementos secundarios.

Cuando una instancia de tarea observa un OperationCanceledException producido por 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 los tokens son iguales y la propiedad del IsCancellationRequested token devuelve true, la tarea interpreta esto como una confirmación de cancelación y transiciona al estado Cancelado. Si no usa un método Wait o WaitAll para esperar a la tarea, entonces la tarea solo establece su estado a Canceled.

Si espera en un elemento Task que cambia al estado Canceled, se crea y se inicia una excepción System.Threading.Tasks.TaskCanceledException (encapsulada en la excepción AggregateException). Esta excepción indica una cancelación correcta en lugar de una situación errónea. Por lo tanto, la propiedad Exception de la tarea devuelve null.

Si la propiedad del IsCancellationRequested 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, lo que hace que la tarea pase al estado Defectuoso. 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 después de solicitar la cancelación.

Consulte también