2009-02-10 9 views
15

A raíz de mi pregunta BeginInvoke()/EndInvoke(), hay diferencias importantes en el rendimiento/cualquier otra cosa entre Delegate. BeginInvoke() y el uso de QueueUserWorkItem() para invocar a un delegado de forma asincrónica?Cuál es la diferencia entre QueueUserWorkItem() y BeginInvoke(), para realizar una actividad asincrónica sin tipos de devolución necesarios

+0

Es posible que también quiera ver: http://marcgravell.blogspot.com/2009/02/async-without-pain.html –

Respuesta

17

http://blogs.msdn.com/cbrumme/archive/2003/07/14/51495.aspx

dice:

"Un hecho sorprendente es que esta es también la razón por Delegate.BeginInvoke/ EndInvoke son tan lentos en comparación con técnicas equivalentes como ThreadPool.QueueUserWorkItem (o UnsafeQueueUserWorkItem si entiende las implicaciones de seguridad y desea ser realmente eficiente). La ruta de código para BeginInv oke/EndInvoke se convierte rápidamente en el código de procesamiento común de mensaje de la ruta de acceso remoto general. "

+0

Hmmm, gracias, eso es interesante y atemorizante. ¡Creo que revertiré cualquier código de BeginInvoke()/EndInvoke() que no requiera valores devueltos! – endian

21

Lo principal que ocurre con QueueUserWorkItem es que usted tiene que utilizar el tipo WaitCallback delegado, que se ve complicado si ya tiene una instancia SomeRandomDelegate y algunos argumentos. La buena noticia es que se puede solucionar este problema con un cierre:

ThreadPool.QueueUserWorkItem(
    delegate { someDelegate(arg1, arg2); } 
); 

Este patrón también se asegura de que obtiene la tipificación fuerte adecuada en tiempo de compilación (a diferencia de pasar un arg object estado para QueueUserWorkItem y echándola en el método de destino). Este patrón también se puede utilizar cuando se llama a métodos directamente:

ThreadPool.QueueUserWorkItem(
    delegate { SomeMethod(arg1, arg2); } 
); 

Obviamente, sin un equivalente EndInvoke, tampoco se puede obtener un valor de retorno de vuelta a menos que se llama a un método/provocar un evento/etc al final de su método ... en una nota relacionada, debe tener cuidado con exception handling.

+3

Al encontrar este escenario exacto, fue la primera vez que pensé para mis adentros: "Guau, me encanta". cierres ". –

+0

Guau, este es un consejo supremo. Se siente como una semántica de boxeo/unboxing, y me imagino que no será más costoso, en un amplio esquema de cosas. –

+0

Vaya, técnicamente, debería ser "delegar (objeto o)" o similar para que el código compile, creo, ya que QueueUserWorkItem espera una firma delegada que coincida con WaitCallback, es decir, una que incluya un solo parámetro de tipo objeto. –

-1

No debería haber ninguna gran diferencia, también creo que el BeginInvoke/EndInvoke generado para un delegado usa el grupo de subprocesos para ejecutar.

-1

No debería haber ninguna diferencia de rendimiento, ya que Delegate.BeginInvoke y ThreadPool.QueueUserWorkItem se ejecutarán en una cadena de subprocesos.

La mayor diferencia es que si llamas a BeginInvoke, estás obligado a llamar a EndInvoke en algún momento. Por el contrario, ThreadPool.QueueUserWorkItem es "disparar y olvidar". Eso tiene beneficios y desventajas. El beneficio es que puedes olvidarte de eso. El inconveniente es que no tiene forma de saberlo, a menos que agregue su propio mecanismo de sincronización/notificación, cuando la tarea se haya completado.

14

El EndInvoke() tiene un comportamiento útil, pero rara vez se menciona - es relanzamientos todas las excepciones no controladas que el delegado generado en el contexto del hilo original para que pueda mover la lógica de procesamiento de excepciones en el código principal.

Además, si su delegado tiene parámetros out/ref, se agregarán a la firma EndInvoke() permitiéndole obtenerlos cuando el método termine la ejecución.

+0

muchas gracias, no sabía eso – endian

4

Si llama a ThreadPool.QueueUserWorkItem, las excepciones planteadas en el elemento de trabajo no se manejarán en el hilo de fondo (a menos que las capture explícitamente). En .Net 2 y superior esto terminará su AppDomain.

Si llama a delegate.BeginInvoke(), las excepciones se ponen en cola para volver a lanzarse cuando se llama a EndInvoke(). Si nunca llama a EndInvoke(), entonces las excepciones son esencialmente memoria 'filtrada' (como cualquier otro estado no liberado por la operación asincrónica).

Cuestiones relacionadas