2010-02-06 10 views
12

Estoy desarrollando una aplicación C# Winforms, parte de la aplicación cargará archivos a un servidor web usando AsyncUpload (usándolo, debido a la necesidad de utilizar una devolución de llamada), en programa en C#C#: Bloqueo de una llamada de función hasta que se cumpla la condición

i consiguió un simple bucle for que llama a la función Carga

for(int i=0;i < 10 ; i++) 
{ 
    Uploadfun(); 
} 

Y la diversión hace un poco de magia:

Uploadfun() 
    { 
    // Logic comes here 

    // webClient.UploadFileAsync runs a 2nd thread to perform upload .. 
    webClient.UploadFileAsync(uri, "PUT", fileNameOnHD); 

} 

Y una devolución de llamada que es llamada w uando la carga asíncrona se realiza

Upload_Completed_callback() 
{ 
    //Callback event 
} 

Editar

La secuencia lógica:

  1. diversión es llamada (de bucle)
  2. lógica de la diversión se ejecuta y realiza ..
  3. Vuelve al ciclo
  4. La devolución de llamada se llamará eventuall y, cuando UploadFileAsync (que ejecuta una lógica en otro hilo) terminará

El problema está en la tercera punto, cuando la ejecución se mueve hacia atrás para el bucle, i necesidad de bloquear el bucle de continuar hasta que el se llama a la devolución de llamada.

+3

¿Tiene acceso a la implementación 'divertida'? Probablemente deberías considerar proporcionar una interfaz síncrona en la que se implemente la API asincrónica. –

+0

En la forma en que lo está describiendo, su código parece ser secuencial y de un solo subproceso, lo que significa que hasta que se llame a la función 'devolución de llamada', el ciclo no continuará. Por favor corrígeme si me equivoco al dar una explicación más detallada. –

+0

@Madi: Entonces estás haciendo esto al revés. Debería usar la versión síncrona de 'UploadFile' en el núcleo de la lógica y usar una API asincrónica encima si lo necesita. –

Respuesta

20

Si entiendo correctamente, quiere llamar al UploadFileAsync y luego bloquear hasta que la llamada asincrónica haya golpeado su devolución de llamada. Si es así, me gustaría usar AutoResetEvent es decir

private readonly AutoResetEvent _signal = new AutoResetEvent(false); 

fun() 
    { 
    // Logic comes here 

    // runs a 2nd thread to perform upload .. calling "callback()" when done 
    webClient.UploadFileAsync(uri, "PUT", fileNameOnHD); 

    _signal.WaitOne(); // wait for the async call to complete and hit the callback  
} 



callback() 
{ 
    //Callback event 
    _signal.Set(); // signal that the async upload completed 
} 

Usando AutoResetEvent significa que el estado se restablece automáticamente después de Set ha sido llamado y un hilo de espera recibe la señal a través WaitOne

+0

al usar Waitone() después de uploadAsync, el programa se está deteniendo y no se está llamando a la Devolución de llamada. –

+0

okie ... arreglé el problema de detención, llamado fun() en un hilo separado, "obviamente" llamarlo en el hilo principal está causando el problema! @Juliet Kinda ayudó a señalar el problema ... gracias a los dos =) –

4

En C# métodos bloquean por defecto, por lo que no debería necesitar hacer nada. Supongo que por algún motivo está llamando a un método sin bloqueo que inicia una tarea en segundo plano/hilo/lo que sea y le da una devolución de llamada cuando termina. Desea llamar a este método asincrónico de forma sincrónica.

Puede llamar al fun desde dentro de la devolución de llamada. Algo a lo largo de estas líneas (pseudo-código):

int n; 

callFunTenTimes() 
{ 
    n = 0; 
    fun(n); 
} 

callback() 
{ 
    ++n; 
    if (n < 10) 
     fun(n); 
    else 
     print("done"); 
} 

Esto es similar a continuation passing style.

Una ventaja de este método es que también puede hacer que su método sea asíncrono sin agregar ningún subproceso adicional, bloqueos o lógica adicional; solo proporciona una función de devolución de llamada a la que su cliente puede suscribirse. Funciona bien en un entorno impulsado por eventos.

+0

Eso es gracioso. He estado pensando en mi propio problema en esta línea: hacer que la función de devolución de llamada sea la 'carne' de lo que solía hacer el ciclo. Esta solución parece mantener el tiempo de espera al mínimo. – Joe

+0

necesito la versión asíncrona porque proporciona una devolución de llamada de "progreso", que es un requisito básico. –

+0

@Madi D: Si el requisito es que la carga se comporte de forma asíncrona, ¿por qué no hacer también que su función sea una función asíncrona con una devolución de llamada? ¿Puedes explicar más sobre para qué estás usando esto? ¿Es esta una aplicación WinForms, ASP.NET, etc.? –

1

El problema está aquí:

for(int i=0;i < 10 ; i++) 
{ 
    fun(); <-- if we block until this function finishes here, we stop the UI thread 
} 

Lo que está haciendo es secuencial.Y si usted no puede permitirse el lujo de bloquear el hilo de interfaz de usuario, mueva el bucle fuera el hilo de interfaz de usuario:

volatile downloadComplete; 

void DownloadUpdates() 
{ 
    ThreadPool.QueueUserWorkItem(state => 
     for(int i = 0; i < 10; i++) 
     { 
      downloadComplete = false; 
      webClient.UploadFileAsync(uri, "PUT", fileNameOnHD); 
      while(!downloadComplete) { Thread.Sleep(1); } 
     }); 
} 

Upload_Completed_callback() 
{ 
    downloadComplete = true; 
} 

Ahora puede bloquear la ejecución del bucle sin detener el hilo de interfaz de usuario, y también obtener el beneficio de indicadores de progreso de la clase webclient.

+0

No use una variable booleana para esto, el compilador puede optimizarla (lo que provocaría el comportamiento de no continuar nunca que el OP menciona). zebrabox mostró la forma correcta de hacerlo, con un objeto de evento waitable. –

+1

@Ben: Agradezco sus comentarios, pero creo que son engañosos. En primer lugar, el compilador no optimizará el bool mientras esté utilizado o asignado en algún lugar (y presumiblemente lo es). En segundo lugar, spin-waiting en bool y resetEvents son formas válidas de bloquear un hilo, ambos son la "forma correcta de hacerlo", ninguno de los dos es "incorrecto", la elección de uno u otro depende de los gustos del programador. – Juliet

+0

Muchas gracias por la respuesta realmente útil, terminé combinando el subproceso de su solución con la solución resetEvent de @ zebrabox, y ahora está funcionando .. =) –

3

Zebrabox tiene razón utilizando un WaitHandle. Si bien la solución de Juliet funciona, el subproceso que realiza el spin-wait consumirá una cantidad significativa de procesador en proporción al WaitHandle, que esencialmente estaría inactivo.

+0

+1 arrojando luz en las dos respuestas principales (para mí) .. –

+0

No consumirá mucho procesador, pero puede evitar que el sistema ponga la CPU en un estado inactivo más profundo y, por lo tanto, reduzca la batería más rápido en un ordenador portátil. –

Cuestiones relacionadas