2010-02-21 18 views
6

Tengo algo de código de la siguiente manera:Silverlight, que trata de las llamadas asíncronas

 foreach (var position in mAllPositions) 
     { 
       DoAsyncCall(position); 
     } 
//I want to execute code here after each Async call has finished 

Entonces, ¿cómo puedo hacer lo anterior?
que podía hacer algo como:

 while (count < mAllPositions.Count) 
     { 
      //Run my code here 
     } 

y el recuento de la subasta después de cada llamada asíncrono se hace ... pero esto no parece ser una buena manera de hacerlo

Algún consejo? ¿Hay algún patrón de diseño para el problema anterior ya que estoy seguro de que es un escenario común?

Respuesta

0

(NB, estoy dándole dos respuestas separadas. Éste se pega lo más cercano a su pregunta como sea posible.)

Su pregunta dice

while{ count >= mAllPositions.Count) 
{ 
    //Run my code here 
} 

pero estoy adivinando que lo que realmente significa es:

while(count < mAllPositions.Count) 
    ; // do nothing -- busy wait until the count has been incremented enough 
// Now run my code here 

??

Si es así, entonces se puede lograr lo mismo de manera más eficiente mediante el uso de un semáforo:

Semaphore TheSemaphore = new Semaphore(0, mAllPositions.Count); 

Como cada llamada asincrónica completa, liberar el semáforo.

TheSemaphore.Release(); 

Antes de ejecutar el código final, asegurar el semáforo ha sido puesto en libertad el número necesario de veces:

for(int i=0; i<mAllPositions.Count; i++) 
    TheSemaphore.WaitOne(); 
// Now run follow-on code. 

Su código se bloqueará hasta que todos se han completado las operaciones asíncronas.

+0

¿Semáforo en Silverlight? –

0

Su pregunta es un poco incierto (en caso de que sea count <=?), Pero aquí va ...

Lo que estamos pidiendo es cómo hacer una llamada sincrónica. Con una llamada asíncrona, antes de realizar la llamada, asigna un controlador de eventos al que se llamará cuando finalice la llamada, luego realiza la llamada. Esto significa que su código de finalización tiene una función diferente al código que realiza la llamada. Esto significa que si su llamada asincrónica se realiza en la secuencia de la interfaz de usuario, la interfaz de usuario no se bloqueará mientras se realiza la llamada.

Las llamadas de sincronización son posibles en Silverlight, pero debe asegurarse de no hacerlas en el hilo de la interfaz de usuario. Una forma de lograr esto es iniciar un nuevo hilo de fondo, ejecutar la llamada asincrónica en el hilo de fondo, pero bloquear el retorno hasta que se complete la llamada. Aquí está un ejemplo de código de pseudo:

private AutoResetEvent myResetEvent; 

private void MyCallFunction(object someParameter) { 

    if (this.Dispatcher.CheckAccess()) 
    { 
     Action<object> a = new Action<object>(MyCallFunction); 
     a.BeginInvoke(someParameter, null, null); 
     return; 
    } 

    myResetEvent = new AutoresetEvent(); 
    myAsyncCall.CallCompleted += new EventHandler<>(myAsyncCall_CallCompleted); 
    myAsyncCall.DoAsyncCall(someParameter); 

    myResetEvent.WaitOne(); 
    //increment your count here 
} 

private void myAsyncCall_CallCompleted(object sender, SomeEventArgs e) { 
    if (e.Error == null && !e.Cancelled) { 
     if (myResetEvent != null) 
      myResetEvent.Set(); 
    } 
} 

Tenga en cuenta que este código no es especialmente hilo de seguridad o producción listo - es sólo una muestra rápida.

Lo que sucede aquí es que cuando ingresa MyCallFunction, comprueba si se está ejecutando en el hilo de la interfaz de usuario, si es así, vuelve a invocarse en una cadena de fondo. Luego configura un AutoResetEvent y realiza la llamada asincrónica.Luego hace una pausa en el objeto myResetEvent hasta que se haya establecido (o 'señalizado') desde el controlador de llamada finalizada, en cuyo punto continúa la ejecución del código. Tenga en cuenta que no debe intentar acceder a un control directamente desde un código como este sin antes asegurarse de que está de nuevo en el hilo de la interfaz de usuario.

Cuando comencé a averiguar cómo hacer esto en Silverlight, comencé con this SO post y continué con this link. Pero como Marc gravell dice en esa primera publicación, no lo hagas si puedes evitarlo. La única vez que tuve que hacer esto fue cuando necesité agregar los resultados de varias llamadas WCF dispares en un resultado que se devolvió a la IU (y estas llamadas no se pudieron combinar en un método WCF de estilo fachada).

0

(NB, estoy dando dos respuestas separadas, lo que lleva a un enfoque totalmente diferente que en la pregunta.)

Siguiendo el ejemplo de código de Miguel Madero en la this URL, utilice el Marco reactiva que esperar en paralelo para todos los resultados para completar, luego continuar con otra tarea.

(Hay otra discusión en la misma cadena de correo electrónico, incluyendo un enlace a this closely related StackOverflow question.)

0

Usted puede hacer grandes cosas, siguiendo el concepto de flujos de trabajo asincrónicos (vaya a la C# párrafo) como se muestra a través del blog de Tomas .

http://tomasp.net/blog/csharp-async.aspx

0
  1. instalación de un AutoResetEvent estática
  2. ThreadPoolQueueUserWorkItems de configuración
  3. artículo ForEach en el bucle, la cola el artículo, y pase en el AutoResetEvent como parámetro de la devolución de llamada.
  4. Dentro de la devolución de llamada, haga el trabajo y luego llame al autoresetevent.Set();
  5. En el cuerpo principal en el punto //I want to execute code here after each Async call has finished, llame al autoResetEvent.Wait() correspondiente;
0

Otra opción es aumentar un contador en el bucle foreach que llama a los procesos asíncronos. Luego, en la verificación de devolución de llamada disminuirá el contador y comprobará que la igualdad sea cero. Cuando el contador ha vuelto a cero, todas las llamadas se completan y se puede ejecutar su próximo código. Solo asegúrate de cambiar el contador de forma segura. La forma más fácil de hacer esto podría ser volver al hilo de la interfaz de usuario antes de decrementar y probar el contador.

0

No veo esto como un problema de "Quiero hacer una llamada sincrónica". Lo único que realmente está haciendo es vincular una serie de llamadas asincrónicas cuando están completas.

He encontrado esto antes en el caso de que tenga una lista de elementos y necesite obtener de forma asincrónica atributos del servidor (algunos para el estado por ejemplo) pero también desea actualizar algún indicador de progreso de carga en la pantalla .

En esta situación, no desea bloquear el subproceso de la interfaz de usuario mientras se actualizan todos los elementos (bloquear los hilos es malo de todos modos si hay una alternativa).

por lo que utiliza una clase de control de la siguiente manera:

public class GroupAction 
{ 
    public delegate void ActionCompletion(); 

    private uint _count = 0; 
    private ActionCompletion _callback; 
    private object _lockObject = new object(); 

    public GroupAction(uint count, ActionCompletion callback) 
    { 
     _count = count; 
     _callback = callback; 
    } 

    public void SingleActionComplete() 
    { 
     lock(_lockObject) 
     { 
      if (_count > 0) 
      { 
       _count--; 
       if (_count == 0) _callback(); 
      } 
     } 
    } 
} 

Se crea esta acción con un recuento (por el número de elementos) y una devolución de llamada que se ejecuta en la terminación de todos los artículos.Básicamente, como un semáforo con más control y sin subprocesos de los que preocuparse.

El código de llamada se vería así (he basado el ejemplo en llamar al servicio web de conversión temporal estándar que puede encontrar en w3schools.com).

private void DoGroupCall() 
    { 
     uint count = 5; 
     GroupAction action = new GroupAction(count, 
      () => 
      { 
       MessageBox.Show("Finished All Tasks"); 
      }); 

     for (uint i = 0; i < count; i++) 
     { 
      TempConvertHttpPost proxy = new TempConvertHttpPostClient(new BasicHttpBinding(), new EndpointAddress("http://localhost/webservices/tempconvert.asmx")); 
      CelsiusToFahrenheitRequest request = new CelsiusToFahrenheitRequest() { Celsius = "100" }; 

      proxy.BeginCelsiusToFahrenheit(request, 
        (ar) => Deployment.Current.Dispatcher.BeginInvoke(
        () => 
         { 
          CelsiusToFahrenheitResponse response = proxy.EndCelsiusToFahrenheit(ar); 

          // Other code presumably... 

          action.SingleActionComplete(); 
         } 
       ), null); 
     } 
    } 

Nota cómo la instancia Groupaction se define con un delegado de devolución de llamada en línea y luego la función SingleCallBack se llama cada vez que devuelve el servicio.

Espero que esto ayude ...

1

Dado que está utilizando Silverlight, y que no tienen acceso a semáforos, usted podría estar buscando algo como esto (escrito en el bloc de notas, así que no hay promesa en relación con sintaxis perfecta):

int completedCallCount = 0; 
int targetCount = mAllPositions.Count; 

using (ManualResetEvent manualResetEvent = new ManualResetEvent(false)) 
{ 
    proxy.DoAsyncCallCompleted += (s, e) => 
    { 
     if (Interlocked.Increment(ref completedCallCount) == targetCount) 
     { 
      manualResetEvent.Set(); 
     } 
    }; 

    foreach (var position in mAllPositions) 
    { 
     proxy.DoAsyncCall(position); 
    } 

    // This will wait until all the events have completed. 
    manualResetEvent.WaitOne(); 
} 

Sin embargo, uno de los beneficios de Silverlight lo que obligó a cargar de forma asíncrona de datos es que usted tiene que trabajar duro para bloquear la interfaz de usuario a la hora de hacer unas llamadas de servicio, que es exactamente lo que Lo voy a hacer con un código como este, a menos que lo ejecute en un hilo de fondo, wh Lo recomiendo encarecidamente. Siempre puede mostrar un mensaje de "espere por favor" hasta que finalice el proceso en segundo plano.

+0

¿Cuál es una buena manera de implementar y usar mensajes "Espere ..." en llamadas asíncronas, y qué pasa si hay varias llamadas asíncronas. – VoodooChild

+0

He escrito Semaphores para Silverlight: http://modosansreves-coding.blogspot.com/2011/08/semaphore-in-silverlight.html –

Cuestiones relacionadas