2010-09-16 16 views
5

Mi código de excepción corre 4 función para completar la información (utilizando la invocación) a una clase como:Parallel.Invoke - manejo

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 
          .... 
         }); 
    } 
} 

Mi pregunta es: No puedo volver nulo si él no es un vegetariano, pero quiero poder detener todos los hilos, detener el procesamiento y simplemente devolver nulo. ¿Cómo puede lograrse?

Respuesta

4

Bueno, puede lanzar una excepción de su acción, coger AggregateException en GetPerson (es decir, poner un bloque try/catch alrededor Parallel.Invoke), la verificación de que sea el tipo de excepción, y devolver null.

Que cumple con todo excepto deteniendo todos los hilos. Creo que es poco probable que pueda detener fácilmente ejecutando tareas a menos que empiece a recibir tokens de cancelación. Puede detener tareas adicionales ejecutando manteniendo un valor boolean para indicar si alguna de las tareas hasta ahora ha fallado, y asegúrese de que cada tarea lo compruebe antes de comenzar ... es algo feo, pero funcionará.

Sospecho que el uso de tareas "completas" en lugar de Parallel.Invoke haría todo esto más elegante.

7

Para salir del Parallel.Invoke tan pronto como sea posible habría que hacer tres cosas:

  1. programar la acción que detecta si desea salir desde la primera acción. Luego se programa más pronto (tal vez como primero, pero eso no está garantizado) para que sepa más rápido si desea salir.
  2. Lanza una excepción cuando detectas el error y tomas un AggregateException como indica la respuesta de Jon.
  3. Usar tokens de cancelación. Sin embargo, esto solo tiene sentido si tiene la oportunidad de verificar su propiedad IsCancellationRequested.

Su código sería entonces el siguiente aspecto:

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. 
} 

Sin embargo, como ya he dicho, los tokens de cancelación sólo tienen sentido si se puede comprobar a. Por lo tanto, debe haber una oportunidad dentro de GetAgeFromWebServiceX para verificar el token de cancelación y salir temprano, de lo contrario, no es lógico pasar tokens a estos métodos.

1

De todas formas, ¿necesita cargar primero su Person de la base de datos? Como es su código, llama a los servicios web con un nulo.

Si su lógica es realmente secuencial, hágalo secuencialmente y solo haga en paralelo lo que tenga sentido.