2009-06-11 1170 views
11

uso el siguiente método en un trozo de código de producción:.NET: ¿Debo mantener una referencia a WebClient mientras descargo de forma asincrónica?

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

Ahora estoy preocupado de que un disco para reproducir fallo podría ser causada por el WebClient ejemplo ser basura recogida antes del evento DownloadDataCompleted se llama: después de salir mi método DownloadData(), no hay referencias obvias al objeto WebClient, por lo que podría ocurrir de manera plausible.

Entonces mi pregunta es: ¿puede esto ocurrir de manera realista? No puedo reproducir el problema, por lo que puede haber algunas cosas internas que impidan que el objeto WebClient sea recolectado como basura (por ejemplo, el objeto puede registrarse con un objeto global en algún lugar mientras espera la respuesta).

El código se está ejecutando en .NET 2.0 si eso hace la diferencia.

Respuesta

5

No sé con certeza si el WebClient normalmente puede ser basura recolectada o no mientras se está llevando a cabo una operación asíncrona, porque puede haber referencias internas, pero la pregunta más importante es: ¿Importa?

Mientras que el número suficiente de WebClient permanezca "vivo" para atender la solicitud y llame a su controlador, ¿importa si el objeto principal WebClient es basura?

La documentación de WebClient no menciona nada sobre tener que retener una referencia (a diferencia del System.Threading.Timer docs, por ejemplo), así que creo que es razonable suponer que está bien.

En este caso particular, su delegado tiene una referencia al WebClient, por lo que siempre que se haga referencia al delegado, el WebClient no puede ser. Mi suposición acertada es que alguna parte del sistema en alguna parte necesita una devolución de llamada para saber qué hacer cuando llega el tráfico de red, y esa devolución de llamada conducirá (indirectamente) a su delegado, por lo que está bien.

+1

Está por supuesto correcto que la pregunta importante es si el delegado obtiene la basura recolectada. Y sí, uno debería pensar que el sistema de finalización de IO debe contener una referencia (indirecta) al delegado. Pero no parece completamente imposible que esta referencia sea una WeakReference. –

+1

Sí, no es imposible. Pero espero * que sea documentado (como lo es para Timer). –

0

Flipside de la moneda ... si almacenó una referencia al WebClient en alguna parte, solo para ver si hace la diferencia ... ¿eso hace que el problema desaparezca por completo? Puede ser más fácil comprobarlo de esta manera y asegurarse de adivinar lo que parece lógico.

1

Puede intentar depurar la aplicación con Herramientas de depuración para Windows: le permite ver exactamente qué es lo que mantiene referencias a un objeto específico (with the appropriate plug-in). Muy útil para tales casos.

No obstante, no conozco la respuesta a tu pregunta. Una posibilidad es que, mientras dure la operación, WebClient se convierta en uno de los objetos "raíz", que nunca se recogen como basura (una aplicación .NET generalmente tiene entre 5 y 10 objetos, que son las raíces de varios árboles de referencia utilizados por la aplicación). Esto es pura especulación, sin embargo.

0

Al crear un método anónimo con una referencia a una variable de ámbito (webClient en su caso) y hacer su propia variable con una referencia a ese objeto. Entonces, como jon está adivinando que su delegado mantendrá una referencia al webClient y antes de que el delegado se anule el registro, el webClient no puede ser recolectado como basura.

Sin embargo, en general, sugiero no utilizar la referencia webClient en su método de delegado, sino convertir el remitente en una variable interna. el uso de variables externas a métodos anónimos puede conducir a errores muy extraños.

10

No, su objeto no será GC-ed hasta que finalice la devolución de llamada. De acuerdo con Does the Garbage Collector destroy temporarily unreferenced objects during async calls in .NET?, " la API asíncrono mantiene una referencia a su solicitud (dentro del grupo de subprocesos donde se alojan las operaciones asíncronas IO) y por lo tanto no se recoge la basura hasta que se complete."

embargo, su código también hace cosas que no necesita: no necesita separar el controlador de eventos y no necesita llamar a Dispose en el webclient. (Dispose() en realidad no está implementado por WebClient-- puede ver esto en la fuente de referencia de .NET Framework en http://referencesource.microsoft.com/netframework.aspx).

Por lo tanto, no necesita consultar la instancia del cliente web en su devolución de llamada. En otras palabras, el siguiente código funcionará igual de bien y evitará cualquier problema potencial (discutido anteriormente) al hacer referencia a variables locales externas desde el interior de un delegado.

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

De todos modos, es probable que desee buscar en otro lugar la fuente de su error. Un lugar en el que miraría es en los resultados de las llamadas HTTP: es posible que se esté quedando sin memoria, que se esté ejecutando un error en el servidor, etc. Puede ver e.Error para ver si las llamadas funcionan realmente.

+0

Olvidó adjuntar el controlador de eventos: 'webClient.DownloadDataCompleted + = eh;' –

+0

corregido. gracias por hacérmelo saber. Es gracioso que nadie más haya visto esto en los 7 años desde que escribí esta respuesta. ;-) –

+0

Je, sí. Tuve una pregunta ayer @ http://stackoverflow.com/questions/39609125/object-disposal-and-garbage-collection-prior-to-event-triggering/39609469#39609469 y se encontró que su respuesta era un recurso valioso. –

Cuestiones relacionadas