.net - Es Delegate.EndInvoke() realmente necesario?
delegates begininvoke (7)
De la documentación de Windows Form en Control.BeginInvoke ()
Puede llamar a EndInvoke para recuperar el valor de retorno del delegado, si es necesario, pero esto no es necesario . EndInvoke se bloqueará hasta que se pueda recuperar el valor de retorno.
Este es el caso particular de la llamada asíncrona de Windows Form en el subproceso de la interfaz de usuario y esto no se aplica al caso general , sin embargo, esto puede ayudar a aquellos en esta situación.
He leído un par de foros e incluso una o dos preguntas de stackoverflow que dicen que Delegate.EndInvoke es necesario cuando se utiliza Delegate.BeginInvoke. Muchos de los artículos que he leído sobre el uso de BeginInvoke no han mencionado el uso de EndInvoke. También implementé el código de producción utilizando solo BeginInvoke y no parece haber problemas de memoria. La forma en que he usado BeginInvoke es generalmente con hilos que no me importan cuando terminan o cuánto tiempo tardan en procesarse.
Del artículo de MSDN "Llamar a métodos sincrónicos de forma asíncrona" :
Independientemente de la técnica que utilice, siempre llame a EndInvoke para completar su llamada asíncrona.
Ahora, hay teoría y luego hay práctica. Ha encontrado, como muchos otros desarrolladores antes que usted, que a menudo puede ignorar este requisito documentado. Puede ser un detalle de la implementación si EndInvoke
realmente hace algo que sea absolutamente necesario para evitar que su aplicación se bloquee, pierda memoria, etc. Pero aquí está la cosa: si es un requisito documentado, realmente debería hacerlo . Esto no es solo acerca de la teoría; Se trata de protegerse en caso de cambio.
Al documentar este requisito, los diseñadores de este mecanismo de llamada asíncrono básicamente se dieron la libertad de cambiar la forma en que BeginInvoke
y EndInvoke
funcionan en la línea de modo que, si hubiera suficientes razones (por ejemplo, una mejora de rendimiento), EndInvoke
podría convertirse repentinamente en una gran cantidad mas necesario Supongamos que de repente resultaría en un punto muerto si lo olvidara. Ya se han cubierto diciendo que siempre llaman a EndInvoke
; Si su aplicación deja de funcionar porque no cumplió con este requisito, la responsabilidad recae en usted.
No estoy diciendo que este es necesariamente un escenario probable. Mi punto es simplemente que no deberías, o al menos no lo haría, preguntar: "¿Es esto realmente necesario?" con la mentalidad de " Si puedo salirme con la suya excluyéndolo, lo haré , ya que está documentado que debes hacerlo".
Es absolutamente necesario si planeas lanzar excepciones a tu hilo y esperas atraparlas adecuadamente. Si no llama a EndInvoke, el hilo que lanza la excepción desaparecerá y no sabrá nada al respecto.
Para admitir EndInvoke, proporcione un AsyncCallback, y en ese método de devolución de llamada, asegúrese de envolver su llamada a EndInvoke con un bloque try / catch.
Si bien no puede hacerlo si no le importa lo que ocurra en el hilo, diría que es un hábito muy bueno para simplemente llamar a EndInvoke. Nunca se sabe, un desarrollador junior podría entrar algún día allí, cambiar el código en su hilo y lanzar una excepción. Luego se implementa la aplicación actualizada y comienzan a llegar las llamadas de servicio.
Escuché algo sobre los problemas de pérdida de memoria que pueden surgir.
Por una búsqueda de palabras clave, encontré una buena discusión.
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.
Aquí está la reference ,
MSDN le dice que es importante llamar a EndInvoke:
Nota importante Independientemente de la técnica que utilice, siempre llame a EndInvoke para completar su llamada asíncrona.
Se ha documentado que EndInvoke
no es obligatorio (no se asignan recursos no administrados, suponiendo que no espere en IAsyncResult
) al usar BeginInvoke
para realizar una acción en el subproceso de la GUI en una aplicación WinForms.
Sin embargo, esta es una excepción específica a la regla general: para cada Begin Operation
debe haber una End Operation
coincidente. Como se indica en otra A aquí: si el código de acceso de la GUI se puede lanzar, necesitará el EndInvoke
para obtener la excepción.
Vea aquí para (una especie de) confirmación oficial: http://blogs.msdn.com/b/cbrumme/archive/2003/05/06/51385.aspx#51395 (es el comentario de Chris Brummie 12 May 2003 5:50pm
.
Adicional: La documentación de Control.BeginInvoke
incluye este comentario:
Puede llamar a
EndInvoke
para recuperar el valor de retorno del delegado, si es necesario, pero esto no es necesario.EndInvoke
se bloqueará hasta que se pueda recuperar el valor de retorno.
Así que es oficial: con WinForm''s usar un delegado asíncrono para realizar una acción en el subproceso de la GUI no requiere EndInvoke
(a menos que necesite el valor de retorno o la posible excepción, en cualquier caso considere usar Invoke
).
La forma en que he usado BeginInvoke es generalmente con hilos que no me importan cuando terminan o cuánto tiempo tardan en procesarse.
Parece que debería considerar el uso de la clase Thread
lugar de delegados asíncronos.
Si le preocupan los resultados o la detección de errores (que requieren que los resultados / los errores se calculen en el subproceso de origen), puede usar delegados asíncronos y, en este caso , necesitará EndInvoke
. Mejor aún, use las Task
o Task<TResult>
.
Si solo desea desactivar una operación independiente , use la clase Thread
.