Delegate.EndInvoke () è davvero necessario?

Ho letto un paio di forum e anche una o due domande stackoverflow che dicono che Delegate.EndInvoke è necessario quando si utilizza Delegate.BeginInvoke. Molti degli articoli che ho letto parlando dell’utilizzo di BeginInvoke non hanno menzionato l’utilizzo di EndInvoke. Inoltre ho distribuito il codice di produzione utilizzando solo BeginInvoke e non sembrano esserci problemi di memoria. Il modo in cui ho usato BeginInvoke è in genere con thread a cui non mi interessa quando finiscono o quanto tempo impiegano per elaborare.

Dall’articolo MSDN “Chiamata sincrona dei metodi in modo asincrono” :

Indipendentemente dalla tecnica utilizzata, chiama sempre EndInvoke per completare la chiamata asincrona.

Ora, c’è la teoria e poi c’è la pratica. Hai trovato, come molti altri sviluppatori prima di te, che puoi spesso farla franca ignorando questo requisito documentato. Potrebbe essere un dettaglio di implementazione se EndInvoke esegue effettivamente tutto ciò che è assolutamente necessario per evitare che l’applicazione si arresti in modo anomalo, perdite di memoria, ecc. Ma ecco la cosa: se si tratta di un requisito documentato, è davvero necessario farlo . Questo non riguarda solo la teoria; si tratta di proteggere te stesso in caso di cambiamento.

Documentando questo requisito, i progettisti di questo meccanismo di chiamata asincrono si sono dati la libertà di modificare il modo in cui BeginInvoke ed EndInvoke funzionano lungo la linea in modo che, se ci fosse un motivo sufficiente (ad esempio, un miglioramento delle prestazioni), EndInvoke potrebbe improvvisamente diventare molto più necessario. Supponiamo che si trasformsrebbe improvvisamente in un punto morto se lo avessi dimenticato. Si sono già coperti dicendo di chiamare sempre EndInvoke ; se la tua app smette di funzionare perché non hai seguito questo requisito, l’onere è su di te.

Non sto dicendo che questo è necessariamente uno scenario probabile. Il mio punto è semplicemente che non dovresti – o almeno non lo farei – “È davvero necessario?” con la mentalità di Se posso farla franca lasciandolo fuori, allora lo farò , dal momento che è documentato che dovresti farlo.

È assolutamente necessario se pianifichi di lanciare eccezioni dalla tua discussione e aspetti di catturarle correttamente. Se non si chiama EndInvoke, il thread che genera l’eccezione svanirà e non si saprà nulla a riguardo.

Per supportare EndInvoke, fornire un AsyncCallback e in tale metodo di callback, assicurarsi di avvolgere la chiamata in EndInvoke con un blocco try / catch.

Mentre potresti non farcela se non ti interessa ciò che accade nel thread, direi che è una buona abitudine entrare a chiamare EndInvoke. Non si sa mai, uno sviluppatore junior potrebbe un giorno entrare e cambiare il codice nel thread e generare un’eccezione. Quindi l’app aggiornata viene distribuita e le chiamate di servizio iniziano ad arrivare.

Ho sentito parlare di problemi di perdita di memoria che possono sorgere.

Con una ricerca per parole chiave, ho trovato una buona discussione.

Does not calling EndInvoke *really* cause a memory leak ?

It can but it won't necessarily. Technically there is no such thing as a memory leak in .NET. Eventually the memory will be reclaimed by the GC. The problem is that it might be around a long time. The reason that you should call EndInvoke is because the results of the invocation (even if there is no return value) must be cached by .NET until EndInvoke is called. For example if the invoked code throws an exception then the exception is cached in the invocation data. Until you call EndInvoke it remains in memory. After you call EndInvoke the memory can be released.

Ecco il riferimento ,

Un altro riferimento

MSDN dice che è importante chiamare EndInvoke:

Nota importante Indipendentemente dalla tecnica utilizzata, chiama sempre EndInvoke per completare la chiamata asincrona.

È stato documentato che EndInvoke non è richiesto (non vengono allocate risorse non gestite, supponendo che non si aspetti su IAsyncResult ) quando si utilizza BeginInvoke per eseguire azioni sul thread della GUI in un’applicazione WinForms.

Tuttavia questa è un’eccezione specifica alla regola generale: per ogni Begin Operation deve esserci una End Operation corrispondente. Come indicato su un’altra A qui: se il codice di accesso della GUI può essere lanciato, avrai bisogno di EndInvoke per ottenere l’eccezione.

Vedi qui per (sorta di) conferma ufficiale: http://blogs.msdn.com/b/cbrumme/archive/2003/05/06/51385.aspx#51395 (è il commento di Chris Brummie 12 May 2003 5:50pm .

Ulteriori informazioni: la documentazione di Control.BeginInvoke include questa nota:

È ansible chiamare EndInvoke per recuperare il valore restituito dal delegato, se necessario, ma non è necessario. EndInvoke bloccherà fino a quando il valore di ritorno può essere recuperato.

Quindi è ufficiale: con WinForm che usa il delegato asincrono per eseguire un’azione sul thread della GUI non richiede EndInvoke (a meno che non si abbia bisogno del valore restituito o della ansible eccezione, in entrambi i casi si consideri l’utilizzo di Invoke ).

Il modo in cui ho usato BeginInvoke è in genere con thread a cui non mi interessa quando finiscono o quanto tempo impiegano per elaborare.

Sembra che dovresti prendere in considerazione l’utilizzo della class Thread anziché dei delegati asincroni.

Se ti interessano i risultati o se rilevi errori (che richiedono entrambi il marshalling dei risultati / errori nel thread di origine), puoi utilizzare i delegati asincroni e in questo caso avresti bisogno di EndInvoke . Meglio ancora, usa le classi Task o Task .

Se vuoi solo eseguire un’operazione indipendente , usa la class Thread .

Dalla documentazione di Windows Form su Control.BeginInvoke ()

È ansible chiamare EndInvoke per recuperare il valore restituito dal delegato, se necessario, ma non è necessario . EndInvoke bloccherà fino a quando il valore di ritorno può essere recuperato.

Questo è il caso particolare della chiamata asincrona di Windows Form sul thread dell’interfaccia utente e questo non si applica al caso generale , tuttavia ciò può essere di aiuto per coloro che si trovano in questa situazione.