web-dev-qa-db-fra.com

Pourquoi ne puis-je pas intercepter une exception du code asynchrone?

Partout où je l'ai lu, le code suivant devrait fonctionner, mais ce n'est pas le cas.

public async Task DoSomething(int x)
{
   try
   {
      // Asynchronous implementation.
      await Task.Run(() => {
      throw new Exception();
      x++;
      });
   }
   catch (Exception ex)
   {
      // Handle exceptions ?
   }
}

Cela dit, je n'attrape rien et j'obtiens une "exception non gérée" provenant de la ligne de "lancer". Je ne sais rien ici.

27
Dror Weiss

L'option "Juste mon code" est activée. Avec ceci activé, il considère l'exception non gérée en ce qui concerne "juste votre code" - car un autre code intercepte l'exception et la place à l'intérieur d'une tâche, pour être ensuite renvoyé lors de l'appel await et intercepté par votre déclaration de capture.

Sans être attaché dans le débogueur, votre instruction catch sera déclenchée et s'exécutera comme prévu. Ou vous pouvez simplement continuer à partir du débogueur et il fonctionnera comme prévu.

La meilleure chose à faire est de désactiver simplement "Juste mon code". OMI, cela cause plus de confusion qu'il n'en vaut la peine.

31
Matt Smith

Comme l'a dit SLaks, votre code fonctionne bien.

Je soupçonne fortement que vous avez trop simplifié votre exemple et que vous avez un async void dans votre code.

Ce qui suit fonctionne bien :

private static void Main(string[] args)
{
    CallAsync();
    Console.Read();
}

public static async void CallAsync()
{
    try
    {
        await DoSomething();
    }
    catch (Exception)
    {
        // Handle exceptions ?
        Console.WriteLine("In the catch");
    }
}

public static Task DoSomething()
{
    return Task.Run(() =>
    {
        throw new Exception();
    });
}

Les éléments suivants ne fonctionnent pas :

private static void Main(string[] args)
{
    CallAsync();
    Console.Read();
}

public static void CallAsync()
{
    try
    {
        DoSomething();
    }
    catch (Exception)
    {
        // Handle exceptions ?
        Console.WriteLine("In the catch");
    }
}

public static async void DoSomething()
{
    await Task.Run(() =>
    {
        throw new Exception();
    });
}

Voir http://msdn.Microsoft.com/en-us/magazine/jj991977.aspx

Les méthodes async void ont différentes sémantiques de gestion des erreurs. Lorsqu'une exception est levée d'une tâche asynchrone ou d'une méthode de tâche asynchrone, cette exception est capturée et placée sur l'objet Task. Avec les méthodes async void, il n'y a pas d'objet Task, donc toutes les exceptions levées d'une méthode async void seront levées directement sur le SynchronizationContext qui était actif lorsque la méthode async void a démarré. La figure 2 illustre que les exceptions générées par les méthodes async void ne peuvent pas être détectées naturellement.

10
ken2k

Votre code ne sera même pas compilé proprement pour le moment, car le x++; l'instruction est inaccessible. Faites toujours attention aux avertissements.

Cependant, après avoir corrigé cela, cela fonctionne bien:

using System;
using System.Threading.Tasks;

class Test
{
    static void Main(string[] args)
    {
        DoSomething(10).Wait();
    }

    public static async Task DoSomething(int x)
    {
        try
        {
            // Asynchronous implementation.
            await Task.Run(() => {
                throw new Exception("Bang!");
            });
        }
        catch (Exception ex)
        {
            Console.WriteLine("I caught an exception! {0}", ex.Message);
        }
    }
}

Production:

I caught an exception! Bang!

(Notez que si vous essayez le code ci-dessus dans une application WinForms, vous aurez un blocage car vous attendriez une tâche qui devait revenir au thread de l'interface utilisateur. Nous sommes d'accord dans une application console comme tâche reprendra sur un thread threadpool.)

Je soupçonne que le problème n'est en fait qu'une question de débogage - le débogueur peut le considérer comme non géré, même s'il est géré.

8
Jon Skeet