Annulation de tâches

Les classes System.Threading.Tasks.Task et System.Threading.Tasks.Task<TResult> prennent en charge l’annulation à l’aide de jetons d’annulation. Pour plus d’informations, consultez Annulation dans les threads managés. Dans les classes de tâche, l’annulation implique une coopération entre le délégué d’utilisateur, qui représente une opération annulable et le code qui a demandé l’annulation. Une annulation réussie implique la demande du code appelant la méthode CancellationTokenSource.Cancel, et le délégué d’utilisateur terminant l’opération dans le délai imparti. Vous pouvez terminer l'opération à l'aide de l'une des options suivantes :

  • Par un retour du délégué. Dans de nombreux scénarios, cette option est suffisante. Toutefois, une instance de tâche annulée de cette façon passe à l’état TaskStatus.RanToCompletion, et non à l’état TaskStatus.Canceled.

  • En levant un OperationCanceledException et en lui passant le jeton sur lequel l'annulation a été demandée. La meilleure façon de procéder est d’utiliser la méthode ThrowIfCancellationRequested. Une tâche annulée de cette façon passe à l’état Annulé, ce que l’appel de code peut utiliser pour vérifier que la tâche a répondu à sa requête d’annulation.

L’exemple suivant montre le modèle de base d’annulation de tâche qui lève l’exception :

Notes

Le jeton est transmis au délégué utilisateur et à l’instance de tâche.

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

Pour obtenir un exemple complet, consultez Comment : annuler une tâche et ses enfants.

Lorsqu’une instance de tâche observe une levée OperationCanceledException par le code utilisateur, elle compare le jeton de l’exception à son jeton associé (celui passé à l’API ayant créé la tâche). Si les jetons sont identiques et si la propriété IsCancellationRequested du jeton retourne la valeur true, la tâche l’interprète comme une acceptation d'annulation et passe à l’état Canceled. Si vous n’utilisez pas une méthode Wait ou WaitAll pour attendre la tâche, la tâche définit uniquement son état sur Canceled.

Si vous attendez une tâche qui passe à l’état Canceled, une exception System.Threading.Tasks.TaskCanceledException (encapsulée dans une exception AggregateException) est levée. Cette exception indique une annulation réussie et non une défaillance. Ainsi, la propriété Exception de la tâche retourne null.

Si la propriété IsCancellationRequested du jeton retourne false ou si le jeton de l'exception ne correspond pas au jeton de la tâche, la valeur OperationCanceledException est traitée comme une exception normale, entraînant ainsi le passage de la tâche à l'état Faulted. La présence d’autres exceptions entraînera le passage de la tâche à l’état Faulted. Vous pouvez obtenir l'état de la tâche terminée dans la propriété Status .

Il est possible qu’une tâche puisse continuer à traiter certains éléments après la demande d’annulation.

Voir aussi