2009-06-11 7 views
11

Io uso il seguente metodo in un pezzo di codice di produzione:.NET: Devo mantenere un riferimento a WebClient durante il download in modo asincrono?

private void DownloadData(Uri uri) 
{ 
    WebClient webClient = new WebClient(); 
    DownloadDataCompletedEventHandler eh = null; 
    eh = delegate(object sender, DownloadDataCompletedEventArgs e) 
     { 
      webClient.DownloadDataCompleted -= eh; 
      ((IDisposable) webClient).Dispose(); 
      OnDataDownloaded(); 
     }; 
    webClient.DownloadDataCompleted += eh; 
    webClient.DownloadDataAsync(uri); 
} 

Ora sono preoccupato che un disco per riprodurre bug potrebbe essere causato dal WebClient esempio essere garbage collection prima dell'evento DownloadDataCompleted si chiama: dopo l'uscita il mio metodo DownloadData(), non ci sono riferimenti ovvi all'oggetto WebClient, in modo che ciò possa avvenire in modo plausibile.

Quindi la mia domanda è: può accadere questo realisticamente? Non riesco a riprodurre il problema, quindi potrebbero esserci alcune cose interne che impediscono che l'oggetto WebClient venga sottoposto a garbage collection (ad esempio, l'oggetto potrebbe registrarsi da qualche parte in un oggetto globale mentre attende la risposta).

Il codice è in esecuzione su .NET 2.0 se questo fa alcuna differenza.

risposta

5

Non so con certezza se lo standard WebClient può essere normalmente raccolto o meno mentre è in corso un'operazione asincrona, perché potrebbero esserci riferimenti interni, ma la domanda più grande è: è importante?

Fintanto che il numero di WebClient rimane "attivo" per soddisfare la richiesta e chiamare il gestore, è importante che l'oggetto principale WebClient sia triturato?

La documentazione WebClient non menziona nulla sul fatto di dover tenere un riferimento (a differenza dello System.Threading.Timer docs, ad esempio) quindi penso che sia ragionevole presumere che questo sia corretto.

In questo caso particolare, il delegato ha un riferimento allo WebClient, quindi fino a quando si fa riferimento al delegato stesso, lo WebClient non può essere. La mia ipotesi istruita è che una parte del sistema in qualche punto debba contenere una richiamata per sapere cosa fare quando il traffico di rete arriva e tale callback alla fine (indirettamente) porta al delegato, quindi stai bene.

+1

Si è ovviamente corretto che la questione importante è se il delegato riceve la garbage collection. E sì, si dovrebbe pensare che il sistema di completamento dell'istruzione elettronica debba contenere un riferimento (indiretto) al delegato. Ma non sembra del tutto impossibile che questo riferimento possa essere un WeakReference. –

+1

Sì, non impossibile. Ma vorrei * spero * che sarebbe documentato (come lo è per Timer). –

0

Flipside of the coin ... se hai memorizzato un riferimento allo WebClient da qualche parte, solo per vedere se fa la differenza ... questo fa sì che il problema scompaia del tutto? Potrebbe essere più facile controllarlo in questo modo ed essere sicuri di indovinare ciò che sembra logico.

1

È possibile provare a eseguire il debug dell'applicazione con Debugging Tools per Windows: consente di vedere quali sono esattamente i riferimenti a un oggetto specifico (with the appropriate plug-in). Molto utile per questi casi.

Non conosco la risposta alla tua domanda, però. Una possibilità è che per la durata dell'operazione, WebClient si trasformi in uno degli oggetti "root", che non vengono mai raccolti (un'applicazione .NET ha generalmente da 5 a 10 oggetti simili, che sono le radici di diversi alberi di riferimento utilizzati dall'applicazione). Questa è pura speculazione, però.

0

Quando si crea un metodo anonimo con un riferimento a una variabile di ambito (WebClient nel caso) e si rende la propria variabile con un riferimento a tale oggetto. Quindi, come jon sta indovinando il tuo delegato manterrà un riferimento al webClient e prima che il delegato lo annulli, il webClient non può essere raccolto.

Tuttavia, in genere suggerisco di non utilizzare il riferimento webClient nel metodo delegato ma di trasmettere il mittente a una variabile interna. usare variabili esternamente a metodi anonimi può portare a bug davvero strani.

10

No, il tuo oggetto non sarà GC-ed fino al completamento della richiamata. Secondo Does the Garbage Collector destroy temporarily unreferenced objects during async calls in .NET?, " l'API asincrona mantiene un riferimento alla vostra richiesta (all'interno del pool di thread in cui vengono presentate le operazioni di IO async) e così non sarà garbage collection fino al completamento."

Ma, il codice sta anche facendo cose che non ha bisogno di: non è necessario staccare il gestore di eventi e non è necessario chiamare Dispose sul webclient. (In effetti, Dispose() non è implementato da WebClient, è possibile vederlo nella fonte di riferimento .NET Framework allo http://referencesource.microsoft.com/netframework.aspx).

Quindi non è effettivamente necessario fare riferimento all'istanza del client Web nella richiamata. In altre parole, il seguente codice funzionerà altrettanto bene ed eviterà qualsiasi problema potenziale (discusso sopra) di riferimento a variabili locali esterne all'interno di un delegato.

private void DownloadData(Uri uri) 
{ 
    WebClient webClient = new WebClient(); 
    DownloadDataCompletedEventHandler eh = null; 
    eh = delegate(object sender, DownloadDataCompletedEventArgs e) 
    { 
     OnDataDownloaded(); 
    }; 
    webClient.DownloadDataCompleted += eh; 
    webClient.DownloadDataAsync(uri); 
} 

In ogni caso, probabilmente si desidera cercare altrove la fonte del proprio errore. Un posto che guarderei è il risultato delle chiamate HTTP: potresti avere una memoria insufficiente, errori di server, ecc. Puoi vedere e.Error per vedere se le chiamate funzionano effettivamente.

+0

Hai dimenticato di collegare il gestore dell'evento: 'webClient.DownloadDataCompleted + = eh;' –

+0

corretto. grazie per avermi fatto sapere. divertente che nessun altro abbia visto questo nei 7 anni da quando ho scritto questa risposta. ;-) –

+0

Heh, si. Ho avuto una domanda yeterday @ http://stackoverflow.com/questions/39609125/object-disposal-and-garbage-collection-prior-to-event-triggering/39609469#39609469 e la tua risposta è risultata essere una risorsa preziosa. –

Problemi correlati