2011-06-29 4 views
11
public class FooDataRepository 
{ 
    private MyServiceReferenceClient Client { get; set; } 

    public void FooClass() 
    { 
     Client = new MyServiceReferenceClient(); 
     Client.SaveFooCompleted += Client_SaveFooCompleted; 
     Client.SaveBarCompleted += Client_SaveBarCompleted; 
    } 

    private void Client_SaveFooCompleted(Object sender, EventArgs e) { ... } 

    private void Client_SaveBarCompleted(Object sender, EventArgs e) { ... } 

    public void SaveFoo(Foo foo) 
    { 
     Client.SaveFooAsync(foo); 

     foreach (var bar in foo.Bars) 
      Client.SaveBarAsync(bar); 
    } 

} 

Quiero hacer algo dentro de la clase FooDataRepository una vez que el SaveFooAsync y todos los métodos SaveBarAsync hayan finalizado. ¿Hay un patrón estándar para tratar de hacer una sola cosa en función del número N de llamadas Async que se completen?¿Existe un patrón estándar a seguir cuando se espera que se complete un número N de métodos asíncronos?

+0

Es mucho más fácil si tiene acceso a los métodos no asíncronos (por ejemplo, SaveFoo() y SaveBar()). ¿Vos si? – Pat

+0

Si quiere una respuesta suficiente para su caso, tendrá que proporcionar más información. No creo que exista un "patrón estándar" para lo que está describiendo, pero Tasks sería la respuesta más cercana. – Pat

+0

Sí, hay métodos no asincrónicos creados cuando genera una referencia de servicio, sin embargo, el punto entero de la asincronización es no tener un gran bloque de espera para llamadas pesadas. Quiero poder actualizar al usuario en tiempo real a medida que se guarda el foo y se guardan todas las barras. – michael

Respuesta

5

Puede usar TaskFactory.ContinueWhenAll para programar un código para que se ejecute cuando se hayan completado todas las tareas.

Task[] tasks = new Task[3] 
{ 
    Task.Factory.StartNew(() => MethodA()), 
    Task.Factory.StartNew(() => MethodB()), 
    Task.Factory.StartNew(() => MethodC()) 
}; 

//This will not block. 
Task.Factory.ContinueWhenAll(tasks, completedTasks => { RunSomeMethod(); }); 

EDIT:

cuanto a su pregunta sobre la redacción método asíncrono telefónicas con tareas si la clase ha Comience métodos/END para llamar al método de un modo asíncrono puede utilizar Task.FromAsync

Alternativamente también puede usar Rx para llamar a varios métodos de forma asincrónica y luego observar cuándo se han completado todos. Eche un vistazo a esta pregunta: Can I shortcut the Begin/End async pattern by using an event to create my AsyncResult? (C# .Net 3.5, WCF)

+0

¿Puedes mostrar el ejemplo en el contexto de mi ejemplo? La razón por la que pregunto es porque no veo que esto funcione ... los métodos que llamo son Async, lo que significa que declararán "completado" de inmediato en lugar de esperar hasta que se llamen. Cuando se llaman las devoluciones de llamada, la llamada original se "completa" ... – michael

+0

@michael - Consulte mi respuesta actualizada. – Giorgi

1

Si puede, use Tasks y luego haga Task.WaitAll. Ej .:

Task[] tasks = new Task[3] 
{ 
    Task.Factory.StartNew(() => MethodA()), 
    Task.Factory.StartNew(() => MethodB()), 
    Task.Factory.StartNew(() => MethodC()) 
}; 

//Block until all tasks complete. 
Task.WaitAll(tasks); 

// Continue on this thread... 

Si usted no tiene acceso a los métodos síncronos, se puede utilizar un método como el que se describe en Tasks and the Event-based Asynchronous Pattern para convertir su biblioteca EAP en uno que utiliza tareas. Alternativamente, Rx proporciona varias formas de tratar este problema.

Básicamente, el mejor consejo para la práctica es usar Tareas si es posible. Si su aplicación requiere un control más detallado, considere seriamente Rx: proporciona filtrado LINQ para eventos y métodos asincrónicos.

0

No sé cuán "estándar" es mi idea, pero acabo de comenzar a utilizar algunas llamadas asincrónicas con WCF Data Services y Silverlight. En algunos casos, tenemos colecciones que son muy similares a las "colecciones" expuestas por el patrón Repositorio. Emitimos una consulta para obtener los artículos de un Servicio de datos WCF, pero luego cada elemento que se devuelve debe "cargarse" (es decir, cada elemento tiene un método Load que se ejecuta de forma asíncrona y puede emitir su propia consulta WCF Data Service) . Durante toda esta carga, cualquiera de nuestras interfaces gráficas que depende de los datos que se cargan (ciertas pestañas) se "bloquea" (se muestra el indicador de progreso) hasta que se carga. En algunos casos, tenemos que cargar dos colecciones porque una tiene una relación con la otra. Estamos utilizando un patrón de devolución de llamada con el Servicio de datos WCF, por lo que después de que se haya llamado a cada una de las devoluciones de llamada (cuando se cargan varias colecciones), sabemos que nuestra tarea de "carga" está completa.

Por lo tanto, la aplicación de nuestro modelo a su caso daría algo como esto (pseudocódigo bruto utilizando su código de ejemplo como punto de partida)

public class FooDataRepository 
{ 
    bool fooCompleted = false; 
    bool barCompleted = false; 
    int barsSaved = 0; 
    int barCount = 0; 

    private MyServiceReferenceClient Client { get; set; } 
    public void FooClass() 
    { 
     Client = new MyServiceReferenceClient(); 
     Client.SaveFooCompleted += Client_SaveFooCompleted; 
     Client.SaveBarCompleted += Client_SaveBarCompleted; 
    } 

    private void Client_SaveFooCompleted(Object sender, EventArgs e) 
    { 
     fooCompleted = true; 
     if (barCompleted) 
     { 
     SaveCompleted(); 
     } 
    } 

    private void Client_SaveBarCompleted(Object sender, EventArgs e) 
    { 
     Interlocked.Increment(barsSaved); 
     barCompleted = barsSaved == barCount; 
     if (fooCompleted) 
     { 
     SaveCompleted(); 
     } 
    } 

    private void SaveCompleted() 
    { 
     //Do whatever you want to do when foo and all bars have been saved 
    } 

    public void SaveFoo(Foo foo) 
    { 
     fooCompleted = barCompleted = false; 
     barCount = foo.Bars.Count; 
     barsSaved = 0; 

     Client.SaveFooAsync(foo); 
     foreach (var bar in foo.Bars) 
      Client.SaveBarAsync(bar); 
    } 
} 

Para ser honesto, no estoy seguro de si se trata de un " buen "patrón o no. Estas llamadas son asincrónicas y tienen una devolución de llamada/evento que se llama/aumenta cuando el trabajo finaliza. Este patrón funciona lo suficientemente bien como para desactivar nuestra barra de progreso cuando se han cargado todos los datos.

No he usado mucho las tareas y no he usado Rx en absoluto, así que no sé cómo se aplican o no a este problema.

¡Buena suerte!

Cuestiones relacionadas