Gestion des exceptions différente entre Task.Run et Task.Factory.StartNew

J’ai rencontré un problème lorsque j’utilisais Task.Factory.StartNew et Task.Factory.StartNew essayé de capturer une exception levée. Dans mon application, je souhaite encapsuler une tâche de longue durée dans un Task.Factory.StartNew(.., TaskCreationOptions.LongRunning);

Toutefois, l’exception n’est pas interceptée lorsque j’utilise Task.Factory.StartNew . Il fonctionne toutefois comme je le souhaite lorsque j’utilise Task.Run , ce qui, à mon avis, n’était qu’un wrapper sur Task.Factory.StartNew (selon, par exemple, cet article MSDN ).

Un exemple de travail est fourni ici, la différence étant que l’exception est écrite sur la console lors de l’utilisation de Task.Run , mais pas lors de l’utilisation de Factory.StartNew .

Ma question serait:
Si j’ai une tâche LongRunning qui a la possibilité de LongRunning exceptions, comment dois-je les gérer dans le code d’appel?

 private static void Main(ssortingng[] args) { Task t = RunLongTask(); t.Wait(); Console.WriteLine(t.Result); Console.ReadKey(); } private async static Task RunLongTask() { try { await RunTaskAsync(); } catch (Exception e) { Console.WriteLine(e); return false; } Console.WriteLine("success"); return true; } private static Task RunTaskAsync() { //return Task.Run(async () => // { // throw new Exception("my exception"); // }); return Task.Factory.StartNew( async () => { throw new Exception("my exception"); }); } 

Votre problème est que StartNew ne fonctionne pas comme Task.Run avec des delegates async . Le type de retour de StartNew est Task (convertible en Task ). La Task “externe” représente le début de la méthode et la Task “interne” représente l’achèvement de la méthode (y compris les éventuelles exceptions).

Pour accéder à la Task interne, vous pouvez utiliser Unwrap . Ou vous pouvez simplement utiliser Task.Run au lieu de StartNew pour le code async . LongRunning est juste un indice d’optimisation et est vraiment optionnel. Stephen Toub a publié un bon article sur la différence entre StartNew et Run et explique pourquoi Run est généralement meilleur pour le code async .

Mise à jour à partir du commentaire @usr ci-dessous: LongRunning ne s’applique qu’au début de la méthode async (jusqu’à la première opération incomplète LongRunning ). Il est donc certainement préférable d’utiliser Task.Run dans ce cas.

Je vais intégrer certains de mes commentaires dans une réponse, car ils se sont révélés utiles:

LongRunning est identique à forcer la création d’un nouveau thread en pratique. Et votre méthode asynchrone n’est probablement pas sur ce thread depuis longtemps (elle est supprimée au premier point d’attente). Vous ne voulez pas LongRunning dans ce cas.

La durée d’exécution de la méthode asynchrone n’a pas d’importance. Le thread est détruit à la toute première attente (il s’agit d’une tâche non terminée).

Le compilateur peut-il utiliser cet indice de quelque manière que ce soit? Le compilateur est généralement incapable d’parsingr votre code de manière majeure. De plus, le compilateur ne sait rien de la TPL. Le TPL est une bibliothèque. Et cette bibliothèque lancera toujours un nouveau fil. Spécifiez LongRunning ssi votre tâche brûlera presque toujours 100% du processeur pendant plusieurs secondes ou bloquera pendant plusieurs secondes avec une probabilité très élevée.

Je suppose que vous ne voulez pas que LongRunning ici parce que si vous LongRunning , pourquoi utilisez-vous async en premier lieu? async consiste à ne pas bloquer mais à sortir du fil.

Cela devrait être possible lors de la première décompression de la tâche:

 await RunTaskAsync().Unwrap(); 

Ou bien:

 await await RunTaskAsync();