Le traitement séquentiel des tâches asynchrones

Assumer suivants du code synchrone:

try
{
    Foo();
    Bar();
    Fubar();
    Console.WriteLine("All done");
}
catch(Exception e) //For illustration purposes only. Catch specific exceptions!
{
    Console.WriteLine(e);
}

Supposons maintenant que toutes ces méthodes ont un Async homologue et je dois utiliser ceux pour une raison quelconque, il suffit d'enrouler le tout dans une nouvelle tâche n'est pas une option.

Comment puis-je obtenir le même comportement?

Ce que je veux dire par "même" est:

  1. Exécuter un gestionnaire pour l'exception, si l'on est jeté.
  2. Arrêter l'exécution de l'une des méthodes suivantes, si une exception est levée.

La seule chose que j'ai été en mesure de venir avec est horrible:

var fooTask = FooAsync();
fooTask.ContinueWith(t => HandleError(t.Exception),
                     TaskContinuationOptions.OnlyOnFaulted);
fooTask.ContinueWith(
    t =>
    {
        var barTask = BarAsync();
        barTask.ContinueWith(t => HandleError(t.Exception),
                             TaskContinuationOptions.OnlyOnFaulted);
        barTask.ContinueWith(
            t =>
            {
                var fubarTask = FubarAsync();
                fubarTask.ContinueWith(t => HandleError(t.Exception),
                                       TaskContinuationOptions.OnlyOnFaulted);
                fubarTask.ContinueWith(
                    t => Console.WriteLine("All done"),
                    TaskContinuationOptions.OnlyOnRanToCompletion);
            }, 
            TaskContinuationOptions.OnlyOnRanToCompletion);
    }, 
    TaskContinuationOptions.OnlyOnRanToCompletion);

Veuillez noter:

  • J'ai besoin d'une solution qui fonctionne avec .NET 4, donc async/await est hors de question. Toutefois, si cela fonctionnerait avec async/await hésitez pas à montrer comment.
  • Je n'ai pas besoin d'utiliser le TPL. Si c'est impossible, avec les TPL une autre approche serait OK, peut-être avec le Réactif Extensions?
  • Ne crois pas que ça serait possible d'écrire une méthode d'assistance que prend un params Action[] paramètre avec une complète/gestionnaire d'erreur à partir de laquelle il construit toute cette méchanceté pour vous?
  • Bonne idée. C'est ce que je vais faire si il n'y a pas de "vrai" solution...
  • Vous devez également ajouter une poursuite pour chaque tâche de gérer Canceled, si l'une des tâches sont annulables.
  • Supposons qu'ils ne peuvent pas être annulées.
  • Ce ne serait pas une Action[], il serait un Task[]. Fondamentalement, un ForEachAsync qui intègre la gestion des exceptions.
  • Non, ce qui serait en fait un Func<Task>[], parce que les tâches ne doit être démarré lorsque la précédente a été un succès.
  • Ouais.
  • Ouais, je suppose; je viens de comprendre le "helper" méthode ne se soucient pas et il suffit de passer dans n'importe quelle action/lambda appel et ne l' Task trucs pour vous en interne, surtout si vous ne voulez pas que les appelants externes pour ajouter peluches/trucs à la tâche ContinueWith ou d'autres membres. (cela pourrait être utile aussi bien, pourrait juste avoir des surcharges pour les deux)
  • Cela semble juste comme un récit alambiqué de la façon de faire de vos méthodes asynchrones synchrone. Si c'est le cas, pourquoi les faire asynchrone pour commencer?
  • Il aurait besoin d'être un Func<Task> parce que la méthode d'assistance des besoins de la tâche, pour être en mesure de relier les fils de la poursuite. Sans que vous auriez besoin d'envelopper le travail dans un StartNew, et qui serait mauvais que vous êtes inutilement bloquer un thread du pool pour la durée.
  • Ils sont orthogonaux concepts. C'est en fait une commune et appropriée en cas d'utilisation d'exécuter un certain nombre d'opérations asynchrones de manière séquentielle. L'idée est que vous n'êtes pas en gardant un fil occupé par l'avoir fais un blocage d'attente sur chaque opération. C'est l'ensemble de la prémisse de la 5.0 async/await fonctionnalité.
  • Pas synchrone. Juste séquentielle.
  • Comment est-ce différent de l'exécution de la trois méthodes synchrones sur un seul thread d'arrière-plan, si? Il n'y a pas de blocage d'attente dans les deux cas.
  • Comme je l'ai dit dans la question, je ne peux pas le faire. La raison la plus simple pour cela est que l'API, je suis en utilisant uniquement les offres de méthodes Asynchrones.
  • Malheureusement avec 4.0 j'ai été obligé de faire essentiellement la même chose que vous avez là-haut. J'ai ajouté une méthode d'aide à nettoyer le fil de code, mais ouais, c'est moche.
  • Vous êtes en supposant que les tâches représentent toutes des CPU de travail. C'est seulement un sous-ensemble des opérations asynchrones. Vous devez également tenir compte de async IO comme un autre grand exemple. Alors que cette tâche est de "lancer" aucun fil ne doit être en cours d'exécution sur tous les.
  • J'espère que vous êtes juste le réglage de votre réponse. Je ne pense pas qu'il devrait être supprimé, il est une solution de contournement possible.
  • Je comprends maintenant. Merci pour l'explication. J'ai quelques idées sur la façon de résoudre ce problème. Je vais voir si je peut pas les transformer en quelque chose de tangible, plus tard ce soir. (Aussi lol @ les gens se plaignent d'être coincé avec .NET 4.0. Nos développeurs refusent de se servir de quelque chose du passé .NET 3.5 ici).
  • Ouais j'ai fait, surtout parce que je pensais que c'était incorrect du fait qu'il ne prévoit pas le comportement souhaité, mais j'ai modifié et restauré en elle.