[TPL] Erreur courante d'utilisation du CancellationToken sur un Parallel.ForEach
Cela fait déjà un certain nombre de fois que je tombe sur un code similaire à celui-ci :
private void DoJobs(IEnumerable<Object> data,CancellationToken token)
{
// Option pour permettre l'annulation des jobs
ParallelOptions options = new ParallelOptions {CancellationToken = token};
Parallel.ForEach(
data,
options,
DoJob);
}
private void DoJob(Object data)
{
// ...
}
Si la méthode Cancel de la CancellationToken Source à l'origine du token est appelée avant le lancement de notre méthode DoJobs, il y aura une exception sur le Parallel.ForEach.
Certain corrigerons le tir en remplaçant leur code par :
private void DoJobs(IEnumerable<Object> data,CancellationToken token)
{
try
{
// Option pour permettre l'annulation des jobs
ParallelOptions options = new ParallelOptions { CancellationToken = token };
Parallel.ForEach(
data,
options,
DoJob);
}
catch (OperationCanceledException ex)
{
Trace.TraceError(ex.Message);
}
}
C'est effectivement une solution. Mais dans le cas où l'on peut savoir si une annulation est en cours avant même de lancer une autre opération, pourquoi n'utiliserions-nous pas plutôt un code tel que celui-ci?
private void DoJobs(IEnumerable<Object> data,CancellationToken token)
{
// Annulation possible
if (token.IsCancellationRequested) return;
// Option pour permettre l'annulation des jobs
ParallelOptions options = new ParallelOptions { CancellationToken = token };
Parallel.ForEach(
data,
options,
DoJob);
}
Bien entendu, les plus prudents iront peut-être vers un
private void DoJobs(IEnumerable<Object> data,CancellationToken token)
{
// Annulation possible
if (token.IsCancellationRequested) return;
try
{
// Option pour permettre l'annulation des jobs
ParallelOptions options = new ParallelOptions { CancellationToken = token };
Parallel.ForEach(
data,
options,
DoJob);
}
catch (OperationCanceledException ex)
{
Trace.TraceError(ex.Message);
}
}
Et vous, comment préférez-vous, jouer vous avec les CancellationToken et leurs sources?
PS : j’ai voulu présenter ici un cas simple. Je ne passe donc pas de token à ma méthode DoJob dans cet exemple.