Question

Cet exemple "échoue":

static async void Main(string[] args)
{
    try
    {
        await TaskEx.Run(() => { throw new Exception("failure"); });
    }
    catch (Exception)
    {
        throw new Exception("success");
    }
}

Autrement dit, l'exception avec le texte "échec" bouillonne.

Ensuite, j'ai essayé cette solution de contournement:

static async void Main(string[] args)
{
    try
    {
        await SafeRun(() => { throw new Exception("failure"); });
    }
    catch (Exception)
    {
        throw new Exception("success");
    }
}

static async Task SafeRun(Action action)
{
    var ex = default(Exception);
    await TaskEx.Run(() =>
    {
        try
        {
            action();
        }
        catch (Exception _)
        {
            ex = _;
        }
    });
    if (ex != default(Exception))
        throw ex;
}

Cela n'a pas aidé non plus.

Je suppose que mon installation de rafraîchissement Async CTP pourrait être interrompue.

Ce code devrait-il fonctionner comme prévu ("succès" bouillonne, pas "échec"), ou n'est-il pas "censé" fonctionner de cette façon.Et sinon, comment contourneriez-vous ce problème?

Était-ce utile?

La solution

Le comportement que vous voyez est probablement un bogue de cas limite ou peut même être correct, s'il n'est pas intuitif. Normalement, lorsque vous invoquez une méthode asynchrone de manière synchrone, elle encapsule une tâche pour qu'elle s'exécute et comme personne n'attend que la tâche se termine, l'exception ne parvient jamais au thread principal. Si vous deviez appeler Main directement, cela réussirait, mais votre environnement d'exécution verrait une exception de «succès» sur un autre thread.

Étant donné que main est le point d'entrée de votre application, il est appelé de manière synchrone et probablement car le point d'entrée ne déclenche pas le comportement de wrapping de Task, de sorte qu'attendre ne s'exécute pas correctement et que TaskEx.Run lance son propre thread, ce qui apparaît dans le runtime comme une exception lancée sur un autre thread.

Si vous deviez exécuter main en tant que méthode async, c'est-à-dire renvoyer un Task (puisqu'un async qui renvoie void ne peut vraiment être appelé que via await) et le bloquer à partir de votre contexte principal synchrone, vous obtiendriez le comportement approprié comme le test ci-dessous illustre:

static async Task Main() {
    try {
        await TaskEx.Run(() => { throw new Exception("failure"); });
    } catch(Exception) {
        throw new Exception("success");
    }
}

static async Task Main2() {
    await Main();
}

[Test]
public void CallViaAwait() {
    var t = Main2();
    try {
        t.Wait();
        Assert.Fail("didn't throw");
    } catch(AggregateException e) {
        Assert.AreEqual("success",e.InnerException.Message);
    }
    }


[Test]
public void CallDirectly() {
    var t = Main();
    try {
        t.Wait();
        Assert.Fail("didn't throw");
    } catch(AggregateException e) {
        Assert.AreEqual("success", e.InnerException.Message);
    }
}

C'est-à-dire les erreurs de tâche avec un AggregateException qui contient l'exception success comme exception interne.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top