¿Cuál es la diferencia entre QueueUserWorkItem () y BeginInvoke (), para realizar una actividad asincrónica sin tipos de devolución necesarios?

A partir de mi pregunta BeginInvoke () / EndInvoke (), ¿existen 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?

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

dice:

“Un hecho sorprendente es que este es también el motivo por el cual Delegate.BeginInvoke / EndInvoke es tan lento en comparación con técnicas equivalentes como ThreadPool.QueueUserWorkItem (o UnsafeQueueUserWorkItem si entiende las implicaciones de seguridad y quiere ser realmente eficiente). El código de acceso para BeginInvoke / EndInvoke rápidamente se convierte en el código de procesamiento de mensajes común de la vía remota general “.

Lo principal que se me ocurre con QueueUserWorkItem es que debe usar el tipo de delegado WaitCallback , que parece complicado si ya tiene una instancia SomeRandomDelegate y algunos argumentos. La buena noticia es que puedes arreglar esto con un cierre:

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

Este patrón también garantiza que obtendrá una escritura fuerte y adecuada en tiempo de comstackción (a diferencia de pasar un arg de estado de object a QueueUserWorkItem y convertirlo en el método de destino). Este patrón también se puede usar al llamar directamente a los métodos:

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

Obviamente, sin un equivalente de EndInvoke , tampoco puede recuperar un valor de retorno a menos que llame a un método / plantee un evento / etc al final de su método … en una nota relacionada, debe tener cuidado con la excepción manejo

EndInvoke () tiene un comportamiento útil, pero rara vez mencionado: vuelve a lanzar todas las excepciones no controladas que el delegado generó en el contexto del hilo original para que pueda mover la lógica de procesamiento de excepciones al 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.

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).

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.

No debe 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.