2009-11-11 16 views
10

¿Por qué el delegado necesita llamar a EndInvoke antes de que el método se dispare? Si necesito llamar a EndInvoke (que bloquea el hilo), entonces no es realmente una llamada asíncrona, ¿verdad?¿Por qué el método de delegado asíncrono requiere llamar a EndInvoke?

Aquí está el código que estoy tratando de ejecutar.

class Program 
    { 
     private delegate void GenerateXmlDelegate(); 

     static void Main(string[] args) 
     { 
      GenerateXmlDelegate worker = new GenerateXmlDelegate(GenerateMainXml); 
      IAsyncResult result = worker.BeginInvoke(null, null); 
     } 

     private static void GenerateMainXml() 
     { 
      Thread.Sleep(10000); 
      Console.WriteLine("GenerateMainXml Called by delegate"); 
     } 
    } 

Respuesta

15

La razón necesita llamar EndInvoke es evitar pérdidas de memoria; .Net almacenará información sobre el resultado de la función (o excepción) hasta que llame al EndInvoke.

Puede llamar al EndInvoke en el controlador de finalización que le da al BeginInvoke y conservar la naturaleza asincrónica.

EDITAR:

Por ejemplo:

class Program { 
    private delegate void GenerateXmlDelegate(); 

    static void Main(string[] args) { 
     GenerateXmlDelegate worker = new GenerateXmlDelegate(GenerateMainXml); 
     IAsyncResult result = worker.BeginInvoke(delegate { 
      try { 
       worker.EndInvoke(); 
      } catch(...) { ... } 
     }, null); 
    } 

    private static void GenerateMainXml() { 
     Thread.Sleep(10000); 
     Console.WriteLine("GenerateMainXml Called by delegate"); 
    } 
} 

Si desea disparar una llamada asincrónica y olvidarse de ello, puede utilizar el ThreadPool, así:

ThreadPool.QueueUserWorkItem(delegate { GenerateMainXml(); }); 
+0

lo siento, ¿podría extender mi ejemplo para demostrar lo que quiere decir con Completion Handler? De todos los artículos que he leído sugiere que simplemente llamando a BeginInvoke se activará la llamada al método. –

+1

Esto es solo algo correcto: no se producirá ninguna pérdida de memoria. https://gist.github.com/jcdickinson/9109599. Sin embargo, en ciertos escenarios, se realiza un seguimiento con los pares 'Begin/End-Invoke'; un ejemplo es: si no se llama' End * 'en las operaciones' Socket', los contadores de rendimiento del socket se perderán por completo (no hay pérdida de memoria, los valores serán completamente incorrectos). –

6

Como dijo SLaks, EndInvoke asegura contra filtraciones de memoria.

BeginInvoke sigue siendo asíncrono; considere el siguiente código:

static void Main() { 
    Func<double> slowCalculator = new Func<double>(PerformSlowCalculation); 
    IAsyncResult slowCalculation = slowCalculator.BeginInvoke(null, null); 

    // lots of stuff to do while slowCalculator is doing its thing 

    Console.WriteLine("Result is {0}", slowCalculator.EndInvoke(slowCalculation)); 
} 

static double PerformSlowCalculation() { 
    double result; 

    // lots and lots of code 

    return result; 
} 

Si este código se escribe sin las BeginInvoke/EndInvoke llamadas, PerformSlowCalculation tendría que terminar antes de Main podría hacer el resto de sus "muchas cosas"; de esta manera, los dos pueden estar sucediendo al mismo tiempo.

Ahora, en su ejemplo usando un GenerateXmlDelegate, sigue siendo necesario EndInvoke a pesar de que usted no está regresando nada. La forma de hacerlo es:

static void Main(string[] args) { 
    GenerateXmlDelegate worker = new GenerateXmlDelegate(GenerateMainXml); 
    IAsyncResult result = worker.BeginInvoke(GenerateXmlComplete, null); 
} 

private static void GenerateXmlComplete(IAsyncResult result) { 
    AsyncResult realResult = result as AsyncResult; 
    GenerateXmlDelegate worker = result.AsyncDelegate as GenerateXmlDelegate; 
    worker.EndInvoke(); 
} 
Cuestiones relacionadas