Comment gérer une exception Task.Run

J’ai eu un problème pour intercepter mon exception dans Task.Run. J’ai modifié mon code et mon problème a été résolu. Je suis disposé à comprendre quelle est la différence entre la gestion des exceptions dans Task.Run de ces deux manières:

Dans la fonction Outside, je ne peux pas attraper l’exception, mais à l’intérieur, je peux l’attraper.

void Outside() { try { Task.Run(() => { int z = 0; int x = 1 / z; }); } catch (Exception exception) { MessageBox.Show("Outside : " + exception.Message); } } void Inside() { Task.Run(() => { try { int z = 0; int x = 1 / z; } catch (Exception exception) { MessageBox.Show("Inside : "+exception.Message); } }); } 

Lorsqu’une tâche est exécutée, toutes les exceptions qu’elle génère sont conservées et rediffusées lorsque quelque chose attend le résultat de la tâche ou son achèvement.

Task.Run() retourne un object Task que vous pouvez utiliser pour le faire, donc:

 var task = Task.Run(...) try { task.Wait(); // Rethrows any exception(s). ... 

Pour les versions plus récentes de C #, vous pouvez utiliser wait à la place de Task.Wait ():

 try { await Task.Run(...); ... 

ce qui est beaucoup plus net.


Pour être complet, voici une application console compilable qui illustre l’utilisation de await :

 using System; using System.Threading; using System.Threading.Tasks; namespace ConsoleApp1 { class Program { static void Main() { test().Wait(); } static async Task test() { try { await Task.Run(() => throwsExceptionAfterOneSecond()); } catch (Exception e) { Console.WriteLine(e.Message); } } static void throwsExceptionAfterOneSecond() { Thread.Sleep(1000); // Sleep is for illustration only. throw new InvalidOperationException("Ooops"); } } } 

L’idée d’utiliser Task.Wait fera l’affaire, mais le thread appelant (comme le dit le code) attendra et bloquera donc jusqu’à ce que la tâche soit finalisée, ce qui rendra le code synchrone au lieu d’async.

Utilisez plutôt l’option Task.ContinueWith pour obtenir des résultats:

 Task.Run(() => { //do some work }).ContinueWith((t) => { if (t.IsFaulted) throw t.Exception; if (t.IsCompleted) //optionally do some work); }); 

Si la tâche doit continuer sur le thread d’interface utilisateur, utilisez l’option TaskScheduler.FromCurrentSynchronizationContext () comme paramètre on continue avec comme suit:

 ).ContinueWith((t) => { if (t.IsFaulted) throw t.Exception; if (t.IsCompleted) //optionally do some work); }, TaskScheduler.FromCurrentSynchronizationContext()); 

Ce code renverra simplement l’exception globale à partir du niveau de la tâche. Bien sûr, vous pouvez également introduire une autre forme de traitement des exceptions ici.

Dans votre code extérieur, vous ne faites que vérifier si le démarrage d’une tâche ne jette pas d’exception ni le corps même de la tâche. Il s’exécute de manière asynchrone et le code qui l’a initié est alors exécuté.

Vous pouvez utiliser:

 void Outside() { try { Task.Run(() => { int z = 0; int x = 1 / z; }).GetAwaiter().GetResult(); } catch (Exception exception) { MessageBox.Show("Outside : " + exception.Message); } } 

À l’aide de .GetAwaiter().GetResult() attend que la tâche se termine et transmet l’exception telle qu’elle est et ne l’enveloppe pas dans AggregateException .

Pour moi, je voulais que mon Task.Run continue après une erreur, laissant l’interface utilisateur la traiter car elle a le temps.

Ma solution (bizarre?) Est aussi d’avoir un Form.Timer en cours d’exécution. Mon Task.Run a sa queue (pour les éléments de longue durée non liés à l’interface utilisateur) et mon Form.Timer a sa queue (pour les éléments d’interface utilisateur).

Etant donné que cette méthode fonctionnait déjà pour moi, il était facile d’append une gestion des erreurs: si le task.Run obtient une erreur, il ajoute les informations sur l’erreur à la file d’attente Form.Timer, qui affiche le dialog d’erreur.

Vous pouvez simplement attendre, puis les exceptions se retrouvent dans le contexte de synchronisation actuel (voir la réponse de Matthew Watson). Ou, comme Menno Jongerius le mentionne, vous pouvez ContinueWith pour garder le code asynchrone. Notez que vous ne pouvez le faire que si une exception est levée à l’aide de l’option de continuation OnlyOnFaulted :

 Task.Run(()=> { //.... some work.... }) // We could wait now, so we any exceptions are thrown, but that // would make the code synchronous. Instead, we continue only if // the task fails. .ContinueWith(t => { // This is always true since we ContinueWith OnlyOnFaulted, // But we add the condition anyway so resharper doesn't bark. if (t.Exception != null) throw t.Exception; }, default , TaskContinuationOptions.OnlyOnFaulted , TaskScheduler.FromCurrentSynchronizationContext());