2010-09-16 6 views
5

Mon code fonctionne 4 fonction de remplir des informations (à l'aide Invoke) à une classe telle que:Parallel.Invoke - Gestion des exceptions

class Person 
{ 
    int Age; 
    string name; 
    long ID; 
    bool isVegeterian 

    public static Person GetPerson(int LocalID) 
    { 
     Person person; 
     Parallel.Invoke(() => {GetAgeFromWebServiceX(person)}, 
         () => {GetNameFromWebServiceY(person)}, 
         () => {GetIDFromWebServiceZ(person)}, 
         () => 
         { 
          // connect to my database and get information if vegeterian (using LocalID) 
          .... 
          if (!person.isVegetrian) 
           return null 
          .... 
         }); 
    } 
} 

Ma question est la suivante: Je ne peux pas retourner null s'il n'est pas un vegeterian, mais je veux pouvoir arrêter tous les threads, arrêter le traitement et juste retourner null. Comment peut-on y arriver?

Répondre

4

Eh bien, vous pouvez lancer une exception de votre action, prendre AggregateException en GetPerson (à savoir mettre un bloc try/catch autour Parallel.Invoke), vérifiez qu'il soit le bon type d'exception, et retourner null.

Cela satisfait à tout sauf en arrêtant tous les threads. Je pense qu'il est peu probable que vous puissiez facilement arrêter déjà en cours d'exécution des tâches sauf si vous commencez à entrer dans les jetons d'annulation. Vous pouvez arrêter d'autres tâches d'exécuter en gardant une valeur boolean pour indiquer si l'une des tâches a échoué jusqu'à présent, et faire vérifier chaque tâche avant de commencer ... c'est un peu moche, mais ça marchera.

Je suppose que l'utilisation de tâches "complètes" au lieu de Parallel.Invoke rendrait tout cela plus élégant.

7

Pour quitter le Parallel.Invoke le plus tôt possible que vous auriez à faire trois choses:

  1. programmez l'action qui détecte si vous voulez sortir dès la première action. Il est alors programmé plus tôt (peut-être en premier, mais ce n'est pas garanti), donc vous saurez plus tôt si vous voulez sortir.
  2. Lève une exception lorsque vous détectez l'erreur et attrape un AggregateException comme l'indique la réponse de Jon.
  3. Utilisez des jetons d'annulation. Cependant, cela n'a de sens que si vous avez la possibilité de vérifier leur propriété IsCancellationRequested.

Votre code serait alors se présenter comme suit:

var cts = new CancellationTokenSource(); 
try 
{ 
    Parallel.Invoke(
     new ParallelOptions { CancellationToken = cts.Token }, 
     () => 
     { 
      if (!person.IsVegetarian) 
      { 
       cts.Cancel(); 
       throw new PersonIsNotVegetarianException(); 
      } 
     }, 
     () => { GetAgeFromWebServiceX(person, cts.Token) }, 
     () => { GetNameFromWebServiceY(person, cts.Token) }, 
     () => { GetIDFromWebServiceZ(person, cts.Token) } 
    ); 
} 
catch (AggregateException e) 
{ 
    var cause = e.InnerExceptions[0]; 
    // Check if cause is a PersonIsNotVegetarianException. 
} 

Cependant, comme je l'ai dit, jetons d'annulation font sens que si vous pouvez les vérifier. Il devrait donc y avoir une possibilité à l'intérieur de GetAgeFromWebServiceX de vérifier le jeton d'annulation et de quitter tôt, sinon, passer des jetons à ces méthodes n'a pas de sens.

1

De toute évidence, vous devez d'abord charger votre Person depuis la base de données? Comme c'est votre code appelle les services Web avec un null.

Si votre logique est vraiment séquentielle, faites-la de manière séquentielle et ne faites en parallèle que ce qui est logique.