Concurrents process

Dans cette dernière partie, nous allons voir comment annuler une opération asynchrone et comment suivre sa progression.

Annuler une opération

Pour annuler une opération asynchrone, .NET fournit les classes CancellationToken et CancellationTokenSource. Pour annuler une méthode asynchrone, il faut lui passer un objet CancellationToken en paramètre. Un CancellationToken est créé de la manière suivante:

var cancellationTokenSource = new CancellationTokenSource(); 
var cancellationToken = cancellationTokenSource.Token; 

Une fois notre CancellationToken créé, on le passe comme paramètre à notre méthode asynchrone. Pour annuler notre traitement, il existe plusieurs méthodes. L’une d’elle consiste à surveiller la valeur de la propriété IsCancellationRequested du token. Pour lancer l’annulation de notre exécution, il faut appeler la méthode Cancel() de notre CancellationTokenSource

Ex:
static void main()
{
    var cancellationTokenSource = new CancellationTokenSource();
    var cancellationToken = cancellationTokenSource.Token;

    Task.Factory.StartNew(() => MethodeLongue(100, cancellationToken));

    Console.WriteLine("Appuyer c pour annuler la tache...");

    var c = Console.ReadKey();

    if (c.KeyChar == 'c')
    {
        cancellationTokenSource.Cancel();
    }

    Console.ReadKey();
}

static void MethodeLongue(int nbTasks, CancellationToken ct) 
{ 
    for(int i = 0; i<nbtasks; i++)
    {
        Console.WriteLine($"étape {i + 1}/{nbTasks}");

        Thread.Sleep(1000); //simule une opération longue

        if (ct.IsCancellationRequested)
         {
              Console.WriteLine("tache annulee");
              return;
         }
    }
}

Une autre méthode consiste à appeler la méthode ThrowIfCancellationRequested() du token. Cet appel lèvera une exception OperationCanceledException, qu’il faudra « catcher » dans la méthode appelante.

Ex:
static void Main(string[] args)
{
    var cancellationTokenSource = new CancellationTokenSource();
    var cancellationToken = cancellationTokenSource.Token;
           
    Task.Factory.StartNew(() => {
                                    try
                                    {
                                        MethodeLongue(100, cancellationToken);
                                    }
                                    catch(OperationCanceledException e)
                                    {
                                        Console.WriteLine("Opération annulée");
                                    }
                                });
         
      Console.WriteLine("Appuyer c pour annuler la tache...");

      var c = Console.ReadKey();

      if (c.KeyChar == 'c')
      {
          cancellationTokenSource.Cancel();
      }

      Console.ReadKey();
}

static void MethodeLongue(int nbTasks, CancellationToken ct)
{
    for (int i = 0; i < nbTasks; i++)
    {
        Console.WriteLine($"étape {i + 1}/{nbTasks}");

        ct.ThrowIfCancellationRequested();

        Thread.Sleep(1000); //simule une opération longue
     }
}

Suivre la progression

Pour suivre l’avancement d’une opération asynchrone, on utilise l’interface IProgress<T>. En fournissant un objet implémentant cet interface à une méthode asynchrone, on peut suivre l’état d’avancement de cette opération. Pour obtenir un objet implémentant IProgress<T> facilement, il suffit de créer une instance de la classe Progress<T>. Voyons comment ça se passe concrètement.

On crée un objet Progress<int>
var progress = new Progress<int>((value) =>
                                            {
                                                Console.WriteLine($"étape {value}/{nbTotal}");
                                            });

On utilise le type int parce que c’est ce type qui est utilisé pour suivre l’avancement de notre processus. On peut en utiliser un autre. Notre objet Progress<int> est utilisé comme paramètre dans notre méthode asynchrone.

static void MaMethodeASuivre(IProgress<int> progress, int nbTasks)
{
    for (int i = 0; i < nbTasks; i++)
    {
        if (progress != null)
        {
            progress.Report(i);
        }

        Thread.Sleep(1000); //simule une opération longue
    }
}

Le code utilisé dans l’expression lambda à la création de notre objet Progress<int> est appelé à chaque fois qu’on appelle la méthode Report de notre Progress<int>.

multithreading33

Conclusion

Cette série d’article nous a permis de voir la richesse du Framework .NET. Il nous offre de nombreuses possibilités pour lancer des processus concurrents très facilement. La liste proposée dans cet article n’est pas exhaustive. En effet il existe d’autres structures qui permettent de gérer les tâches parallèles comme les Barrier.

Auteur : Daniel MINKO FASSINOU

Laisser un commentaire




Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.