Pourquoi TaskCanceledException se produit-il?

J’ai le code de test suivant:

void Button_Click(object sender, RoutedEventArgs e) { var source = new CancellationTokenSource(); var tsk1 = new Task(() => Thread1(source.Token), source.Token); var tsk2 = new Task(() => Thread2(source.Token), source.Token); tsk1.Start(); tsk2.Start(); source.Cancel(); try { Task.WaitAll(new[] {tsk1, tsk2}); } catch (Exception ex) { // here exception is caught } } void Thread1(CancellationToken token) { Thread.Sleep(2000); // If the following line is enabled, the result is the same. // token.ThrowIfCancellationRequested(); } void Thread2(CancellationToken token) { Thread.Sleep(3000); } 

Dans les méthodes de thread, je ne jette aucune exception, mais je reçois TaskCanceledException dans le bloc try-catch du code externe qui lance les tâches. Pourquoi cela se produit et à quoi sert token.ThrowIfCancellationRequested(); dans ce cas. Je crois que l’exception ne devrait être levée que si j’appelle token.ThrowIfCancellationRequested(); dans la méthode de thread.

Je crois que cela est un comportement attendu parce que vous rencontrez une variation de la condition de course.

De Comment: annuler une tâche et ses enfants :

Le thread appelant ne met pas fin à la tâche avec force; il signale seulement que l’annulation est demandée. Si la tâche est déjà en cours, il appartient à l’utilisateur délégué de noter la demande et de répondre de manière appropriée. Si l’annulation est demandée avant l’exécution de la tâche, le délégué de l’utilisateur n’est jamais exécuté et l’object de tâche passe à l’état Canceled .

et de l’ annulation de tâche :

Vous pouvez terminer l’opération en retournant simplement […] depuis le délégué. Dans de nombreux scénarios, cela suffit; cependant, une instance de tâche qui est “annulée” de cette manière passe à l’état RanToCompletion , et non à l’état Canceled .

Ma supposition .Start() est que lorsque vous appelez .Start() sur vos deux tâches, il y a des chances que l’une (ou les deux) n’ait pas réellement démarré avant d’avoir appelé .Cancel() sur votre CancellationTokenSource . Je parie que si vous placez au moins trois secondes d’attente entre le début des tâches et l’annulation, cela ne fera pas exception. Vous pouvez également vérifier la propriété .Status des deux tâches. Si j’ai raison, la propriété TaskStatus.Canceled doit lire TaskStatus.Canceled sur au moins l’un d’entre eux lorsque l’exception est levée.

N’oubliez pas que le démarrage d’une nouvelle Task ne garantit pas la création d’un nouveau thread. Il incombe à la TPL de décider de ce qui reçoit un nouveau thread et de ce qui est simplement mis en queue pour exécution.