2009-11-11 5 views
10

Perché il delegato deve chiamare EndInvoke prima che il metodo venga attivato? Se ho bisogno di chiamare EndInvoke (che blocca il thread), allora non è una chiamata asincrona?Perché il metodo di delega asincrono richiede la chiamata a EndInvoke?

Ecco il codice che sto cercando di eseguire.

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

risposta

15

Il motivo è necessario chiamare EndInvoke è quello di evitare le perdite di memoria; .Net memorizzerà le informazioni sul risultato della funzione (o eccezione) fino a quando non si chiama EndInvoke.

È possibile chiamare EndInvoke nel gestore di completamento che si fornisce a BeginInvoke e mantenere la natura asincrona.

EDIT:

Ad esempio:

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

Se si vuole sparare una chiamata asincrona e non pensarci più, è possibile utilizzare il ThreadPool, in questo modo:

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

scusa potresti estendere il mio esempio per dimostrare cosa intendi per Completion Handler? Da tutti gli articoli che ho letto suggerisce che semplicemente chiamando BeginInvoke si attiva la chiamata al metodo. –

+1

Questo è solo un po 'corretto - non si verificheranno perdite di memoria. https://gist.github.com/jcdickinson/9109599. In alcuni scenari, tuttavia, alcune tracce vengono eseguite con coppie 'Begin/End-Invoke' - un esempio è: se non si chiama' End * 'su operazioni' Socket' i contatori delle prestazioni del socket andranno completamente fuori whack (nessuna perdita di memoria, i valori saranno semplicemente errati). –

6

Come detto SLaks, EndInvoke assicura contro perdite di memoria.

BeginInvoke è ancora asincrono; considerare il seguente codice:

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

Se questo codice sono stati scritti senza i BeginInvoke/EndInvoke chiamate, PerformSlowCalculation avrebbero dovuto finire prima Main potrebbe fare il resto dei suoi "un sacco di roba"; in questo modo, i due possono accadere contemporaneamente.

Ora, nel tuo esempio utilizzando GenerateXmlDelegate, hai ancora bisogno di EndInvoke anche se non stai restituendo nulla. Il modo per farlo è:

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(); 
} 
Problemi correlati