3
votes

Renvoyer les résultats des tâches - y compris les exceptions?

Le code suivant exécute M1 , M2 , M3 , M4 en parallèle. Chaque méthode peut soulever des exceptions. La méthode doit renvoyer les résultats des quatre méthodes asynchrones - soit les int renvoyés par les méthodes, soit les exceptions.

"M1: 1, M2: Excpetion...., M3: 3, M4: 4"

Comment capturer les exceptions individuelles de la tâche qui a échoué?

Par exemple, si seulement M2 a lancé une exception,

async Task<string> RunAll()
{
    int m1result, m2result, m3result, m4result;
    try
    {
        var m1task = M1();
        var m2task = M2();
        var m3task = M3();
        var m4task = M4();
        // await Task.WhenAll(new Task<int>[] { m1task, m2task, m3task, m4task });
        m1result = await m1task;
        m2result = await m2task;
        m3result = await m3task;
        m4result = await m4task;
    }
    catch (Exception ex)
    {
        // need to return the ex of the failed task. How?
    }
    // How to implement M1HasException, M2HasException, ... in the following lines?
    var m1msg = M1HasException ? M1ExceptionMessage : m1result.ToString();
    var m2msg = M2HasException ? M2ExceptionMessage : m2result.ToString();
    var m3msg = M3HasException ? M3ExceptionMessage : m3result.ToString();
    var m4msg = M4HasException ? M4ExceptionMessage : m4result.ToString();
    return $"M1: {m1msg}, M2: {m2msg}, M3: {m3msg}, M4: {m4msg}";
}


4 commentaires

Task.WhenAll ?


J'ai essayé Task.WhenAll () , cela ne semble pas aider à déterminer quelle tâche a obtenu l'exception. La question a été mise à jour.


Je recommande d'examiner cette réponse: stackoverflow.com/a/5383408/5312245


Un exemple reproductible minimal serait génial ici.


4 Réponses :


1
votes

Chaque tâche a une propriété Status et et Exception.

Vous voudrez peut-être voir s'il a fait une faute:

if (myTask.Exception != null) 

Ou s'il a excepté:

myTask.Status == TaskStatus.Faulted

Vous peut utiliser ContinueWhenAll pour exécuter toutes les tâches, puis vérifier l'état.

Consultez la documentation ici .


1 commentaires

Le problème est que dès que, par exemple, M2 a obtenu une exception, M3 et M4 ont cessé d'être invoqués.



0
votes

Pourriez-vous encapsuler chaque wait dans le bloc try-catch et capturer le message d'exception le cas échéant, semble faisable ...

try { await Task.WhenAll(t1, t2, t3); } catch { };
//                                      ^^^^^^^^^
// then same as ^ above

si vous souhaitez utiliser WhenAll , vous pouvez attendre pour cela et ignorer les exceptions, puis faire le même exercice que ci-dessus pour récupérer les résultats de chaque tâche ...

var results = new List<string>();

try { results.Add(await t1); } catch { results.Add("Exception"); };
try { results.Add(await t2); } catch { results.Add("Exception"); };
try { results.Add(await t3); } catch { results.Add("Exception"); };

return string.Join("|", results);


0 commentaires

0
votes

Comme l'ont souligné d'autres réponses / commentaires, une approche possible consiste à utiliser ContinueWith ou ContinueWhenAll . C'est une astuce intelligente car Task a la propriété Exception :

Obtient l'exception AggregateException qui a entraîné la fin prématurée de la tâche. Si la tâche s'est terminée avec succès ou n'a pas encore lancé de exceptions, cela renverra null.

En utilisant ContinueWith , qu'une tâche se termine avec succès ou non, elle sera transmise comme argument à la fonction déléguée. De là, vous pouvez vérifier si une exception a été levée.

Task<string> GetStringedResult<T>(Task<T> initialTask)
{
    return initialTask.ContinueWith(t => {
        return t.Exception?.InnerException.Message ?? t.Result.ToString();
    });
}

async Task<string> RunAll()
{
    string m1result, m2result, m3result, m4result;

    var m1task = GetStringedResult(M1());
    var m2task = GetStringedResult(M2());
    var m3task = GetStringedResult(M3());
    var m4task = GetStringedResult(M4());

    m1result = await m1task;
    m2result = await m2task;
    m3result = await m3task;
    m4result = await m4task;

    return $"M1: {m1result}, M2: {m2result}, M3: {m3result}, M4: {m4result}";
}


0 commentaires

0
votes

Vous pouvez encapsuler les tâches dans un WaitAll et attraper l ' AggregateException ( docs ),

try
{
    Task.WaitAll(new[] { task1, task2 }, token);
}
catch (AggregateException ae)
{
    foreach (var ex in ae.InnerExceptions)
        //Do what ever you want with the ex.
}


0 commentaires