web-dev-qa-db-fra.com

Comment utiliser la propriété CancellationToken?

Comparé au code précédent pour la classe RulyCanceler , je voulais exécuter du code avec CancellationTokenSource.

Comment puis-je l’utiliser comme indiqué dans jetons d’annulation , c’est-à-dire sans lancer/attraper une exception? Puis-je utiliser la propriété IsCancellationRequested?

J'ai essayé de l'utiliser comme ça:

cancelToken.ThrowIfCancellationRequested();

et

try
{
  new Thread(() => Work(cancelSource.Token)).Start();
}
catch (OperationCanceledException)
{
  Console.WriteLine("Canceled!");
}

mais cela a donné une erreur d'exécution sur cancelToken.ThrowIfCancellationRequested(); dans la méthode Work(CancellationToken cancelToken):

System.OperationCanceledException was unhandled
  Message=The operation was canceled.
  Source=mscorlib
  StackTrace:
       at System.Threading.CancellationToken.ThrowIfCancellationRequested()
       at _7CancellationTokens.Token.Work(CancellationToken cancelToken) in C:\xxx\Token.cs:line 33
       at _7CancellationTokens.Token.<>c__DisplayClass1.<Main>b__0() in C:\xxx\Token.cs:line 22
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException:

Le code que j'ai exécuté avec succès a capturé l'opération OperationCanceledException dans le nouveau thread:

using System;
using System.Threading;
namespace _7CancellationTokens
{
  internal class Token
  {
    private static void Main()
    {
      var cancelSource = new CancellationTokenSource();
      new Thread(() =>
      {
         try
         {
           Work(cancelSource.Token); //).Start();
         }
         catch (OperationCanceledException)
         {
            Console.WriteLine("Canceled!");
         }
         }).Start();

      Thread.Sleep(1000);
      cancelSource.Cancel(); // Safely cancel worker.
      Console.ReadLine();
    }
    private static void Work(CancellationToken cancelToken)
    {
      while (true)
      {
        Console.Write("345");
        cancelToken.ThrowIfCancellationRequested();
      }
    }
  }
}
100
Fulproof

Vous pouvez implémenter votre méthode de travail comme suit:

private static void Work(CancellationToken cancelToken)
{
    while (true)
    {
        if(cancelToken.IsCancellationRequested)
        {
            return;
        }
        Console.Write("345");
    }
}

C'est ça. Vous devez toujours gérer l'annulation par vous-même - quitter la méthode au moment opportun (pour que votre travail et vos données soient dans un état cohérent)

PDATE: Je préfère ne pas écrire while (!cancelToken.IsCancellationRequested) car il y a souvent peu de points de sortie où vous pouvez arrêter l'exécution en toute sécurité sur le corps de la boucle, et les boucles ont généralement une condition logique collection etc.). Je pense donc qu'il vaut mieux ne pas mélanger ces conditions car elles ont des intentions différentes.

127
Sasha

@ BrainSlugs83

Vous ne devriez pas faire aveuglément confiance à tout ce qui est posté sur stackoverflow. Le commentaire dans le code Jens est incorrect, le paramètre ne contrôle pas si des exceptions sont levées ou non.

MSDN est très clair ce que ce paramètre contrôle, l'avez-vous lu? http://msdn.Microsoft.com/en-us/library/dd321703 (v = vs.110) .aspx

Si throwOnFirstException est à true, une exception se propage immédiatement hors de l'appel à Cancel, empêchant ainsi le traitement des rappels restants et des opérations annulables. Si throwOnFirstException a la valeur false, cette surcharge agrégera toutes les exceptions émises dans une exception AggregateException, de sorte qu'un rappel renvoyant une exception n'empêche pas l'exécution d'autres rappels enregistrés.

Le nom de la variable est également incorrect, car Cancel est appelé sur CancellationTokenSource et non par le jeton lui-même, et la source change l'état de chaque jeton géré.

24
user3285954

Vous pouvez utiliser ThrowIfCancellationRequested sans gérer l'exception!

L'utilisation de ThrowIfCancellationRequested est censée être utilisée depuis une Task (pas un Thread). Lorsqu'il est utilisé dans une tâche, vous n'avez pas à gérer l'exception vous-même (et obtenez l'erreur d'exception non gérée). Cela entraînera la sortie de la tâche et la propriété Task.IsCancelled sera True. Pas de traitement d'exception nécessaire.

Dans votre cas spécifique, changez le fil de discussion en tâche.

try
{
  Task.Run(() => Work(cancelSource.Token), cancelSource.Token);
}
if (t.IsCancelled)
  Console.WriteLine("Canceled!");
}
10
Titus

Vous pouvez créer une tâche avec un jeton d'annulation. Lorsque vous appliquez l'arrière-plan, vous pouvez annuler ce jeton.

Vous pouvez le faire dans PCL https://developer.xamarin.com/guides/xamarin-forms/application-fundamentals/app-lifecycle

var cancelToken = new CancellationTokenSource();
Task.Factory.StartNew(async () => {
    await Task.Delay(10000);
    // call web API
}, cancelToken.Token);

//this stops the Task:
cancelToken.Cancel(false);

Anther solution est l'utilisateur Timer in Xamarin.Forms, arrêtez le chronomètre lorsque l'application est en arrière-plan https://xamarinhelp.com/xamarin-forms-timer/

9
Jesse Jiang

Vous devez passer le CancellationToken à la tâche, qui surveillera périodiquement le jeton pour voir si l'annulation est demandée.

CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
CancellationToken token = cancellationTokenSource.Token;  
Task task = Task.Run(() => {     
  while(!token.IsCancellationRequested) {
      Console.Write("*");         
      Thread.Sleep(1000);
  }
}, token);
Console.WriteLine("Press enter to stop the task"); 
Console.ReadLine(); 
cancellationTokenSource.Cancel(); 

Dans ce cas, l'opération prendra fin lorsque l'annulation sera demandée et la Task aura un état RanToCompletion. Si vous souhaitez être reconnu que votre tâche a été annulée, vous devez utiliser ThrowIfCancellationRequested pour générer une exception OperationCanceledException.

Task task = Task.Run(() =>             
{                 
    while (!token.IsCancellationRequested) {
         Console.Write("*");                      
        Thread.Sleep(1000);                 
    }           
    token.ThrowIfCancellationRequested();               
}, token)
.ContinueWith(t =>
 {
      t.Exception?.Handle(e => true);
      Console.WriteLine("You have canceled the task");
 },TaskContinuationOptions.OnlyOnCanceled);  

Console.WriteLine("Press enter to stop the task");                 
Console.ReadLine();                 
cancellationTokenSource.Cancel();                 
task.Wait(); 

J'espère que cela aide à mieux comprendre.

3
Mahbubur Rahman