Annulation dans les threads managésCancellation in Managed Threads

.NET Framework 4.NET Framework 4 et versions ultérieures utilisent un modèle unifié pour l'annulation coopérative des opérations asynchrones ou des opérations synchrones de longue durée.Starting with the .NET Framework 4.NET Framework 4, the .NET Framework uses a unified model for cooperative cancellation of asynchronous or long-running synchronous operations. Ce modèle est basé sur un objet léger appelé jeton d'annulation.This model is based on a lightweight object called a cancellation token. L'objet qui appelle une ou plusieurs opérations annulables, par exemple en créant de nouveaux threads ou de nouvelles tâches, passe le jeton à chaque opération.The object that invokes one or more cancelable operations, for example by creating new threads or tasks, passes the token to each operation. Chaque opération peut, à son tour, passer des copies du jeton à d'autres opérations.Individual operations can in turn pass copies of the token to other operations. Ultérieurement, l'objet qui a créé le jeton peut l'utiliser pour demander que les opérations arrêtent leur action.At some later time, the object that created the token can use it to request that the operations stop what they are doing. Seul l'objet demandeur peut émettre la demande d'annulation. Chaque écouteur est chargé d'accepter la demande et d'y répondre de manière appropriée et en temps voulu.Only the requesting object can issue the cancellation request, and each listener is responsible for noticing the request and responding to it in an appropriate and timely manner.

Le modèle général d’implémentation du modèle d’annulation coopérative est le suivant :The general pattern for implementing the cooperative cancellation model is:

  • Instanciez un objet CancellationTokenSource qui gère et envoie une notification d'annulation pour chaque jeton d'annulation.Instantiate a CancellationTokenSource object, which manages and sends cancellation notification to the individual cancellation tokens.

  • Passez le jeton retourné par la propriété CancellationTokenSource.Token à chaque tâche ou thread qui écoute l'annulation.Pass the token returned by the CancellationTokenSource.Token property to each task or thread that listens for cancellation.

  • Fournissez un mécanisme pour chaque tâche ou thread pour répondre à l'annulation.Provide a mechanism for each task or thread to respond to cancellation.

  • Appelez la méthode CancellationTokenSource.Cancel pour fournir une notification d'annulation.Call the CancellationTokenSource.Cancel method to provide notification of cancellation.

Important

La classe CancellationTokenSource implémente l'interface IDisposable.The CancellationTokenSource class implements the IDisposable interface. Quand vous aurez terminé d'utiliser la source du jeton d'annulation, vous devrez appeler la méthode CancellationTokenSource.Dispose pour libérer les ressources non managées qu'elle contient.You should be sure to call the CancellationTokenSource.Dispose method when you have finished using the cancellation token source to free any unmanaged resources it holds.

L'illustration suivante montre la relation entre une source de jeton et toutes les copies de ce jeton.The following illustration shows the relationship between a token source and all the copies of its token.

CancellationTokenSource et CancellationTokensCancellationTokenSource and cancellation tokens

Le nouveau modèle d’annulation facilite la création d’applications et de bibliothèques prenant en charge l’annulation. De plus, il prend en charge les fonctionnalités suivantes :The new cancellation model makes it easier to create cancellation-aware applications and libraries, and it supports the following features:

  • L'annulation est coopérative et n'est pas imposée à l'écouteur.Cancellation is cooperative and is not forced on the listener. L'écouteur choisit comment s'arrêter correctement en réponse à une demande d'annulation.The listener determines how to gracefully terminate in response to a cancellation request.

  • La demande est différente de l'écoute.Requesting is distinct from listening. Un objet qui appelle une opération annulable peut contrôler à quel moment l'annulation est demandée (le cas échéant).An object that invokes a cancelable operation can control when (if ever) cancellation is requested.

  • L'objet demandeur émet la demande d'annulation vers toutes les copies du jeton à l'aide d'un seul appel de méthode.The requesting object issues the cancellation request to all copies of the token by using just one method call.

  • Un écouteur peut écouter plusieurs jetons simultanément en les rassemblant sous la forme d'un même jeton lié.A listener can listen to multiple tokens simultaneously by joining them into one linked token.

  • Le code utilisateur peut remarquer et répondre aux demandes d'annulation à partir du code de bibliothèque, et ce dernier peut remarquer et répondre aux demandes d'annulation à partir du code utilisateur.User code can notice and respond to cancellation requests from library code, and library code can notice and respond to cancellation requests from user code.

  • Les écouteurs peuvent être avertis des demandes d'annulation par le biais d'une interrogation, d'une inscription de rappel ou bien en attendant des handles d'attente.Listeners can be notified of cancellation requests by polling, callback registration, or waiting on wait handles.

Types d'annulationCancellation Types

L'infrastructure d'annulation est implémentée comme un ensemble de types connexes, qui sont répertoriés dans le tableau suivant.The cancellation framework is implemented as a set of related types, which are listed in the following table.

Nom de typeType name DescriptionDescription
CancellationTokenSource Objet qui crée un jeton d'annulation et émet également la demande d'annulation pour toutes les copies de ce jeton.Object that creates a cancellation token, and also issues the cancellation request for all copies of that token.
CancellationToken Type valeur léger passé à un ou plusieurs écouteurs, généralement sous la forme d'un paramètre de méthode.Lightweight value type passed to one or more listeners, typically as a method parameter. Les écouteurs surveillent la valeur de la propriété IsCancellationRequested du jeton par le biais d'interrogations, de rappels ou de handles d'attente.Listeners monitor the value of the IsCancellationRequested property of the token by polling, callback, or wait handle.
OperationCanceledException Les surcharges du constructeur de cette exception acceptent CancellationToken comme paramètre.Overloads of this exception's constructor accept a CancellationToken as a parameter. Les écouteurs peuvent éventuellement lever cette exception pour vérifier la source de l'annulation et informer les autres qu'elle a répondu à une demande d'annulation.Listeners can optionally throw this exception to verify the source of the cancellation and notify others that it has responded to a cancellation request.

Le nouveau modèle d'annulation est intégré à .NET Framework.NET Framework dans plusieurs types.The new cancellation model is integrated into the .NET Framework.NET Framework in several types. Les plus importants sont System.Threading.Tasks.Parallel, System.Threading.Tasks.Task,System.Threading.Tasks.Task<TResult> et System.Linq.ParallelEnumerable.The most important ones are System.Threading.Tasks.Parallel, System.Threading.Tasks.Task, System.Threading.Tasks.Task<TResult> and System.Linq.ParallelEnumerable. Nous vous recommandons d'utiliser ce nouveau modèle d'annulation pour tout nouveau code de bibliothèque et d'application.We recommend that you use this new cancellation model for all new library and application code.

Exemple de codeCode Example

Dans l'exemple suivant, l'objet demandeur crée un objet CancellationTokenSource, puis passe sa propriété Token à l'opération annulable.In the following example, the requesting object creates a CancellationTokenSource object, and then passes its Token property to the cancelable operation. L'opération qui reçoit la demande surveille la valeur de la propriété IsCancellationRequested du jeton par le biais d'une interrogation.The operation that receives the request monitors the value of the IsCancellationRequested property of the token by polling. Quand la valeur devient true, l'écouteur peut s'arrêter de quelque manière appropriée que ce soit.When the value becomes true, the listener can terminate in whatever manner is appropriate. Dans cet exemple, la méthode s'arrête, ce qui suffit dans de nombreux cas.In this example, the method just exits, which is all that is required in many cases.

Note

L'exemple utilise la méthode QueueUserWorkItem pour montrer que la nouvelle infrastructure d'annulation est compatible avec les API héritées.The example uses the QueueUserWorkItem method to demonstrate that the new cancellation framework is compatible with legacy APIs. Pour obtenir un exemple qui utilise le nouveau type préféré System.Threading.Tasks.Task, voir Comment : annuler une tâche et ses enfants.For an example that uses the new, preferred System.Threading.Tasks.Task type, see How to: Cancel a Task and Its Children.

using System;
using System.Threading;

public class Example
{
   public static void Main()
   {
      // Create the token source.
      CancellationTokenSource cts = new CancellationTokenSource();

      // Pass the token to the cancelable operation.
      ThreadPool.QueueUserWorkItem(new WaitCallback(DoSomeWork), cts.Token);
      Thread.Sleep(2500);

      // Request cancellation.
      cts.Cancel();
      Console.WriteLine("Cancellation set in token source...");
      Thread.Sleep(2500);
      // Cancellation should have happened, so call Dispose.
      cts.Dispose();
   }

   // Thread 2: The listener
   static void DoSomeWork(object obj)
   {
      CancellationToken token = (CancellationToken)obj;

      for (int i = 0; i < 100000; i++) {
         if (token.IsCancellationRequested)
         {
            Console.WriteLine("In iteration {0}, cancellation has been requested...",
                              i + 1);
            // Perform cleanup if necessary.
            //...
            // Terminate the operation.
            break;
         }
         // Simulate some work.
         Thread.SpinWait(500000);
      }
   }
}
// The example displays output like the following:
//       Cancellation set in token source...
//       In iteration 1430, cancellation has been requested...
Imports System.Threading

Module Example
   Public Sub Main()
      ' Create the token source.
      Dim cts As New CancellationTokenSource()

      ' Pass the token to the cancelable operation.
      ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf DoSomeWork), cts.Token)
      Thread.Sleep(2500)
        
      ' Request cancellation by setting a flag on the token.
      cts.Cancel()
      Console.WriteLine("Cancellation set in token source...")
      Thread.Sleep(2500)
      ' Cancellation should have happened, so call Dispose.
      cts.Dispose()
   End Sub

   ' Thread 2: The listener
   Sub DoSomeWork(ByVal obj As Object)
      Dim token As CancellationToken = CType(obj, CancellationToken)
      
      For i As Integer = 0 To 1000000
         If token.IsCancellationRequested Then
            Console.WriteLine("In iteration {0}, cancellation has been requested...",
                              i + 1)
            ' Perform cleanup if necessary.
            '...
            ' Terminate the operation.
            Exit For
         End If
      
         ' Simulate some work.
         Thread.SpinWait(500000)
      Next
   End Sub
End Module
' The example displays output like the following:
'       Cancellation set in token source...
'       In iteration 1430, cancellation has been requested...

Annulation d'opération et annulation d'objetOperation Cancellation Versus Object Cancellation

Dans la nouvelle infrastructure d'annulation, l'annulation fait référence aux opérations, et non aux objets.In the new cancellation framework, cancellation refers to operations, not objects. La demande d'annulation signifie que l'opération doit s'arrêter dès que possible après l'exécution de tout nettoyage nécessaire.The cancellation request means that the operation should stop as soon as possible after any required cleanup is performed. Un jeton d'annulation doit faire référence à une opération annulable. Toutefois, cette opération peut être implémentée dans votre programme.One cancellation token should refer to one "cancelable operation," however that operation may be implemented in your program. Après avoir défini la propriété IsCancellationRequested du jeton sur true, celle-ci ne peut pas être réinitialisée à la valeur false.After the IsCancellationRequested property of the token has been set to true, it cannot be reset to false. Les jetons d'annulation ne peuvent donc pas être réutilisés après avoir été annulés.Therefore, cancellation tokens cannot be reused after they have been canceled.

Si vous avez besoin d'un mécanisme d'annulation d'objets, vous pouvez le baser sur le mécanisme d'annulation d'opérations en appelant la méthode CancellationToken.Register, comme indiqué dans l'exemple suivant.If you require an object cancellation mechanism, you can base it on the operation cancellation mechanism by calling the CancellationToken.Register method, as shown in the following example.

using System;
using System.Threading;

class CancelableObject
{
   public string id;
   
   public CancelableObject(string id)
   {
      this.id = id;
   }
   
   public void Cancel() 
   { 
      Console.WriteLine("Object {0} Cancel callback", id);
      // Perform object cancellation here.
   }
}

public class Example
{
   public static void Main()
   {
      CancellationTokenSource cts = new CancellationTokenSource();
      CancellationToken token = cts.Token;

      // User defined Class with its own method for cancellation
      var obj1 = new CancelableObject("1");
      var obj2 = new CancelableObject("2");
      var obj3 = new CancelableObject("3");

      // Register the object's cancel method with the token's
      // cancellation request.
      token.Register(() => obj1.Cancel());
      token.Register(() => obj2.Cancel());
      token.Register(() => obj3.Cancel());

      // Request cancellation on the token.
      cts.Cancel();
      // Call Dispose when we're done with the CancellationTokenSource.
      cts.Dispose();
   }
}
// The example displays the following output:
//       Object 3 Cancel callback
//       Object 2 Cancel callback
//       Object 1 Cancel callback
Imports System.Threading

Class CancelableObject
   Public id As String
   
   Public Sub New(id As String)
      Me.id = id
   End Sub
   
   Public Sub Cancel() 
      Console.WriteLine("Object {0} Cancel callback", id)
      ' Perform object cancellation here.
   End Sub
End Class

Module Example
   Public Sub Main()
        Dim cts As New CancellationTokenSource()
        Dim token As CancellationToken = cts.Token

        ' User defined Class with its own method for cancellation
        Dim obj1 As New CancelableObject("1")
        Dim obj2 As New CancelableObject("2")
        Dim obj3 As New CancelableObject("3")

        ' Register the object's cancel method with the token's
        ' cancellation request.
        token.Register(Sub() obj1.Cancel())
        token.Register(Sub() obj2.Cancel())
        token.Register(Sub() obj3.Cancel())

        ' Request cancellation on the token.
        cts.Cancel()
        ' Call Dispose when we're done with the CancellationTokenSource.
        cts.Dispose()
   End Sub
End Module
' The example displays output like the following:
'       Object 3 Cancel callback
'       Object 2 Cancel callback
'       Object 1 Cancel callback

Si un objet prend en charge plusieurs opérations annulables simultanées, passez un jeton en tant qu'entrée à chaque opération annulable.If an object supports more than one concurrent cancelable operation, pass a separate token as input to each distinct cancelable operation. De cette façon, une opération peut être annulée sans affecter les autres.That way, one operation can be cancelled without affecting the others.

Demandes d'annulation : écoute et réponseListening and Responding to Cancellation Requests

Dans le délégué utilisateur, l'implémenteur d'une opération annulable détermine la façon de terminer l'opération en réponse à une demande d'annulation.In the user delegate, the implementer of a cancelable operation determines how to terminate the operation in response to a cancellation request. Dans de nombreux cas, le délégué utilisateur peut simplement effectuer le nettoyage nécessaire, puis être immédiatement retourné.In many cases, the user delegate can just perform any required cleanup and then return immediately.

Toutefois, dans des cas plus complexes, le délégué utilisateur devra notifier le code de bibliothèque qu'une annulation s'est produite.However, in more complex cases, it might be necessary for the user delegate to notify library code that cancellation has occurred. Dans ce cas, il convient de terminer l'opération en appelant le délégué de la méthode ThrowIfCancellationRequested, ce qui entraînera la levée de OperationCanceledException.In such cases, the correct way to terminate the operation is for the delegate to call the ThrowIfCancellationRequested, method, which will cause an OperationCanceledException to be thrown. Le code de bibliothèque peut intercepter cette exception sur le thread du délégué utilisateur et examiner le jeton de l'exception pour déterminer si l'exception indique une annulation coopérative ou une autre situation exceptionnelle.Library code can catch this exception on the user delegate thread and examine the exception's token to determine whether the exception indicates cooperative cancellation or some other exceptional situation.

La classe Task gère OperationCanceledException de cette façon.The Task class handles OperationCanceledException in this way. Pour plus d’informations, voir Annulation de tâches.For more information, see Task Cancellation.

Écoute par interrogationListening by Polling

Pour les calculs de longue durée qui effectuent des boucles récursives ou non, vous pouvez écouter une demande d'annulation en interrogeant régulièrement la valeur de la propriété CancellationToken.IsCancellationRequested.For long-running computations that loop or recurse, you can listen for a cancellation request by periodically polling the value of the CancellationToken.IsCancellationRequested property. Si sa valeur est de true, la méthode doit effectuer un nettoyage et se terminer aussi rapidement que possible.If its value is true, the method should clean up and terminate as quickly as possible. La fréquence d'interrogation optimale varie selon le type d'application.The optimal frequency of polling depends on the type of application. Il incombe au développeur de déterminer la meilleure fréquence d'interrogation pour un programme donné.It is up to the developer to determine the best polling frequency for any given program. L'interrogation elle-même n'altère pas beaucoup les performances.Polling itself does not significantly impact performance. L'exemple suivant montre une méthode d'interrogation.The following example shows one possible way to poll.

static void NestedLoops(Rectangle rect, CancellationToken token)
{
   for (int x = 0; x < rect.columns && !token.IsCancellationRequested; x++) {
      for (int y = 0; y < rect.rows; y++) {
         // Simulating work.
         Thread.SpinWait(5000);
         Console.Write("{0},{1} ", x, y);
      }

      // Assume that we know that the inner loop is very fast.
      // Therefore, checking once per row is sufficient.
      if (token.IsCancellationRequested) {
         // Cleanup or undo here if necessary...
         Console.WriteLine("\r\nCancelling after row {0}.", x);
         Console.WriteLine("Press any key to exit.");
         // then...
         break;
         // ...or, if using Task:
         // token.ThrowIfCancellationRequested();
      }
   }
}
Shared Sub NestedLoops(ByVal rect As Rectangle, ByVal token As CancellationToken)
    For x As Integer = 0 To rect.columns
        For y As Integer = 0 To rect.rows
            ' Simulating work.
            Thread.SpinWait(5000)
            Console.Write("0' end block,1' end block ", x, y)
        Next

        ' Assume that we know that the inner loop is very fast.
        ' Therefore, checking once per row is sufficient.
        If token.IsCancellationRequested = True Then
            ' Cleanup or undo here if necessary...
            Console.WriteLine(vbCrLf + "Cancelling after row 0' end block.", x)
            Console.WriteLine("Press any key to exit.")
            ' then...
            Exit For
            ' ...or, if using Task:
            ' token.ThrowIfCancellationRequested()
        End If
    Next
End Sub

Pour obtenir un exemple plus complet, voir Comment : écouter les demandes d'annulation par l'interrogation.For a more complete example, see How to: Listen for Cancellation Requests by Polling.

Écoute par inscription de rappelListening by Registering a Callback

Certaines opérations peuvent être bloquées et ne plus pouvoir vérifier la valeur des jetons d'annulation en temps voulu.Some operations can become blocked in such a way that they cannot check the value of the cancellation token in a timely manner. Dans ce cas, vous pouvez inscrire une méthode de rappel qui débloque la méthode quand une demande d'annulation est reçue.For these cases, you can register a callback method that unblocks the method when a cancellation request is received.

La méthode Register retourne un objet CancellationTokenRegistration utilisé spécialement à cet effet.The Register method returns a CancellationTokenRegistration object that is used specifically for this purpose. L'exemple suivant montre comment utiliser la méthode Register pour annuler une requête web asynchrone.The following example shows how to use the Register method to cancel an asynchronous Web request.

using System;
using System.Net;
using System.Threading;

class Example
{
    static void Main()
    {
        CancellationTokenSource cts = new CancellationTokenSource();

        StartWebRequest(cts.Token);

        // cancellation will cause the web 
        // request to be cancelled
        cts.Cancel();
    }

    static void StartWebRequest(CancellationToken token)
    {
        WebClient wc = new WebClient();
        wc.DownloadStringCompleted += (s, e) => Console.WriteLine("Request completed.");

        // Cancellation on the token will 
        // call CancelAsync on the WebClient.
        token.Register(() =>
        {
            wc.CancelAsync();
            Console.WriteLine("Request cancelled!");
        });

        Console.WriteLine("Starting request.");
        wc.DownloadStringAsync(new Uri("http://www.contoso.com"));
    }
}
Imports System.Net
Imports System.Threading

Class Example
    Private Shared Sub Main()
        Dim cts As New CancellationTokenSource()

        StartWebRequest(cts.Token)

        ' cancellation will cause the web 
        ' request to be cancelled
        cts.Cancel()
    End Sub

    Private Shared Sub StartWebRequest(token As CancellationToken)
        Dim wc As New WebClient()
        wc.DownloadStringCompleted += Function(s, e) Console.WriteLine("Request completed.")

        ' Cancellation on the token will 
        ' call CancelAsync on the WebClient.
        token.Register(Function() 
        wc.CancelAsync()
        Console.WriteLine("Request cancelled!")

End Function)

        Console.WriteLine("Starting request.")
        wc.DownloadStringAsync(New Uri("http://www.contoso.com"))
    End Sub
End Class

L'objet CancellationTokenRegistration gère la synchronisation des threads et s'assure que le rappel cessera de s'exécuter à un point précis dans le temps.The CancellationTokenRegistration object manages thread synchronization and ensures that the callback will stop executing at a precise point in time.

Pour garantir la réactivité du système et éviter les interblocages, les instructions suivantes doivent être suivies lors de l'inscription des rappels :In order to ensure system responsiveness and to avoid deadlocks, the following guidelines must be followed when registering callbacks:

  • La méthode de rappel doit être rapide, car elle est appelée de façon synchrone. L'appel à Cancel ne sera donc pas retourné avant le retour du rappel.The callback method should be fast because it is called synchronously and therefore the call to Cancel does not return until the callback returns.

  • Si vous appelez Dispose pendant l'exécution du rappel et détenez un verrou que le rappel attend, votre programme peut connaître un interblocage.If you call Dispose while the callback is running, and you hold a lock that the callback is waiting on, your program can deadlock. Après le retour de Dispose, vous pourrez libérer toutes les ressources requises par le rappel.After Dispose returns, you can free any resources required by the callback.

  • Les rappels ne doivent pas exécuter n'importe quel thread manuel ou n'importe quelle utilisation de SynchronizationContext dans un rappel.Callbacks should not perform any manual thread or SynchronizationContext usage in a callback. Si un rappel doit s'exécuter sur un thread particulier, utilisez le constructeur System.Threading.CancellationTokenRegistration qui permet de spécifier que la cible syncContext est le SynchronizationContext.Current actif.If a callback must run on a particular thread, use the System.Threading.CancellationTokenRegistration constructor that enables you to specify that the target syncContext is the active SynchronizationContext.Current. L'exécution de threads manuels dans un rappel peut entraîner un interblocage.Performing manual threading in a callback can cause deadlock.

Pour obtenir un exemple plus complet, voir Comment : enregistrer des rappels pour les demandes d'annulation.For a more complete example, see How to: Register Callbacks for Cancellation Requests.

Écoute à l'aide d'un handle d'attenteListening by Using a Wait Handle

Quand une opération annulable risque de se bloquer en attendant une primitive de synchronisation, telle que System.Threading.ManualResetEvent ou System.Threading.Semaphore, vous pouvez utiliser la propriété CancellationToken.WaitHandle pour que l'opération attende à la fois l'événement et la demande d'annulation.When a cancelable operation can block while it waits on a synchronization primitive such as a System.Threading.ManualResetEvent or System.Threading.Semaphore, you can use the CancellationToken.WaitHandle property to enable the operation to wait on both the event and the cancellation request. Le handle d'attente du jeton d'annulation sera signalé en réponse à une demande d'annulation et la méthode pourra utiliser la valeur de retour de la méthode WaitAny pour déterminer s'il s'agit de l'annulation du jeton à l'origine du signalement.The wait handle of the cancellation token will become signaled in response to a cancellation request, and the method can use the return value of the WaitAny method to determine whether it was the cancellation token that signaled. L'opération peut alors simplement s'arrêter ou lever une OperationCanceledException, selon le cas.The operation can then just exit, or throw a OperationCanceledException, as appropriate.

// Wait on the event if it is not signaled.
int eventThatSignaledIndex =
       WaitHandle.WaitAny(new WaitHandle[] { mre, token.WaitHandle },
                          new TimeSpan(0, 0, 20));
' Wait on the event if it is not signaled.
Dim waitHandles() As WaitHandle = { mre, token.WaitHandle }
Dim eventThatSignaledIndex =
    WaitHandle.WaitAny(waitHandles, _
                       New TimeSpan(0, 0, 20))

Dans le nouveau code qui cible .NET Framework 4.NET Framework 4, System.Threading.ManualResetEventSlim et System.Threading.SemaphoreSlim prennent en charge la nouvelle infrastructure d'annulation dans leurs méthodes Wait.In new code that targets the .NET Framework 4.NET Framework 4, System.Threading.ManualResetEventSlim and System.Threading.SemaphoreSlim both support the new cancellation framework in their Wait methods. Vous pouvez passer CancellationToken à la méthode. Quand l'annulation sera demandée, l'événement se réveillera et lèvera une OperationCanceledException.You can pass the CancellationToken to the method, and when the cancellation is requested, the event wakes up and throws an OperationCanceledException.

try
{
    // mres is a ManualResetEventSlim
    mres.Wait(token);
}
catch (OperationCanceledException)
{
    // Throw immediately to be responsive. The
    // alternative is to do one more item of work,
    // and throw on next iteration, because
    // IsCancellationRequested will be true.
    Console.WriteLine("The wait operation was canceled.");
    throw;
}

Console.Write("Working...");
// Simulating work.
Thread.SpinWait(500000);
Try
   ' mres is a ManualResetEventSlim
    mres.Wait(token)
Catch e As OperationCanceledException
    ' Throw immediately to be responsive. The
    ' alternative is to do one more item of work,
    ' and throw on next iteration, because
    ' IsCancellationRequested will be true.
    Console.WriteLine("Canceled while waiting.")
    Throw
End Try

 ' Simulating work.
Console.Write("Working...")
Thread.SpinWait(500000)

Pour obtenir un exemple plus complet, voir Comment : écouter les demandes d'annulation avec des handles d'attente.For a more complete example, see How to: Listen for Cancellation Requests That Have Wait Handles.

Écoute simultanée de plusieurs jetonsListening to Multiple Tokens Simultaneously

Dans certains cas, un écouteur peut avoir à écouter simultanément plusieurs jetons d'annulation.In some cases, a listener may have to listen to multiple cancellation tokens simultaneously. Par exemple, une opération annulable peut avoir à surveiller un jeton d'annulation interne en plus d'un jeton passé de manière externe comme argument à un paramètre de méthode.For example, a cancelable operation may have to monitor an internal cancellation token in addition to a token passed in externally as an argument to a method parameter. Pour ce faire, créez une source de jeton lié rassemblant plusieurs jetons au sein d'un même jeton, comme illustré dans l'exemple suivant.To accomplish this, create a linked token source that can join two or more tokens into one token, as shown in the following example.

public void DoWork(CancellationToken externalToken)
{
   // Create a new token that combines the internal and external tokens.
   this.internalToken = internalTokenSource.Token;
   this.externalToken = externalToken;

   using (CancellationTokenSource linkedCts =
           CancellationTokenSource.CreateLinkedTokenSource(internalToken, externalToken))
   {
       try {
           DoWorkInternal(linkedCts.Token);
       }
       catch (OperationCanceledException) {
           if (internalToken.IsCancellationRequested) {
               Console.WriteLine("Operation timed out.");
           }
           else if (externalToken.IsCancellationRequested) {
               Console.WriteLine("Cancelling per user request.");
               externalToken.ThrowIfCancellationRequested();
           }
       }
   }
}
Public Sub DoWork(ByVal externalToken As CancellationToken)
   ' Create a new token that combines the internal and external tokens.
   Dim internalToken As CancellationToken = internalTokenSource.Token
   Dim linkedCts As CancellationTokenSource =
   CancellationTokenSource.CreateLinkedTokenSource(internalToken, externalToken)
   Using (linkedCts)
      Try
         DoWorkInternal(linkedCts.Token)
      Catch e As OperationCanceledException
         If e.CancellationToken = internalToken Then
            Console.WriteLine("Operation timed out.")
         ElseIf e.CancellationToken = externalToken Then
            Console.WriteLine("Canceled by external token.")
            externalToken.ThrowIfCancellationRequested()
         End If
      End Try
   End Using
End Sub

Notez que vous devez appeler Dispose sur la source de jeton lié quand vous en avez terminé avec lui.Notice that you must call Dispose on the linked token source when you are done with it. Pour obtenir un exemple plus complet, voir Comment : écouter plusieurs demandes d'annulation.For a more complete example, see How to: Listen for Multiple Cancellation Requests.

Coopération entre du code de bibliothèque et du code utilisateurCooperation Between Library Code and User Code

L'infrastructure d'annulation unifiée permet au code de bibliothèque d'annuler du code utilisateur, et au code utilisateur d'annuler du code de bibliothèque de façon coopérative.The unified cancellation framework makes it possible for library code to cancel user code, and for user code to cancel library code in a cooperative manner. Pour une coopération harmonieuse, chaque côté doit respecter les recommandations suivantes :Smooth cooperation depends on each side following these guidelines:

  • Si le code de bibliothèque fournit des opérations annulables, il doit également fournir des méthodes publiques qui acceptent un jeton d'annulation externe pour que le code utilisateur puisse demander une annulation.If library code provides cancelable operations, it should also provide public methods that accept an external cancellation token so that user code can request cancellation.

  • Si le code de bibliothèque émet un appel dans le code utilisateur, le code de bibliothèque doit interpréter un OperationCanceledException(externalToken) comme une annulation coopérative, et pas nécessairement comme une exception d'échec.If library code calls into user code, the library code should interpret an OperationCanceledException(externalToken) as cooperative cancellation, and not necessarily as a failure exception.

  • Les délégués utilisateurs doivent tenter de répondre aux demandes d'annulation du code de bibliothèque en temps voulu.User-delegates should attempt to respond to cancellation requests from library code in a timely manner.

System.Threading.Tasks.Task et System.Linq.ParallelEnumerable sont des exemples de classes qui suivent ces recommandations.System.Threading.Tasks.Task and System.Linq.ParallelEnumerable are examples of classes that follows these guidelines. Pour plus d’informations, consultez Annulation de tâches et Procédure : annulation d’une requête PLINQ.For more information, see Task Cancellationand How to: Cancel a PLINQ Query.

Voir aussiSee also