2009-03-26 13 views
26

En la programación multiproceso de .NET, ¿cuáles son los criterios de decisión para usar ThreadPool.QueueUserWorkItem frente a iniciar mi propio hilo a través de los nuevos Thread() y Thread.Start()?Ventaja de usar Thread.Start vs QueueUserWorkItem

En una aplicación de servidor (digamos, una aplicación ASP.NET o un servicio WCF), creo que ThreadPool siempre está ahí y disponible. ¿Qué pasa con una aplicación cliente, como una aplicación WinForms o WPF? ¿Hay algún costo para aumentar el grupo de subprocesos? Si solo quiero que 3 o 4 hilos funcionen durante un período breve en algún cálculo, ¿es mejor QUWI o Thread.Start()?

Respuesta

21

El ThreadPool siempre está ahí, sin embargo, hay un número finito de subprocesos asignados al grupo en función del número de procesadores. Para las aplicaciones ASP.NET, generalmente es una mala idea usar Threads a menos que realmente esté comenzando un proceso Async y sepa que no habrá un gran número de solicitudes (recuerde que hay un número finito de hilos en ThreadPool que compartes con todo lo que se ejecuta en tu AppDomain y hay un límite realista en el número total de subprocesos que deseas crear usando el nuevo Thread() también).

Para las aplicaciones de WinForms, considere utilizar BackgroundWorker en lugar de utilizar un Thread o ThreadPool. Utiliza el ThreadPool, sin embargo, hace que la comunicación entre hilos sea más fácil en el programador inteligente de múltiples hilos.

Actualizado

Evitar el uso de ThreadPool.QueueUserWorkItem como su grupo de aplicación podría desaparecer en cualquier momento. Mueva este trabajo afuera o use WebBackgrounder si es necesario.

De Hanselman.com - "Checklist: What NOT to do in ASP.NET"

4
+0

Realmente no me beneficié de esa discusión, que parecía ser principalmente un debate sobre el costo de comenzar un hilo. – Cheeso

+0

Pensé que el enlace era genial. No vi nada sobre el costo de iniciar un hilo en la respuesta aceptada, pero varias limitaciones de la lista de temas se enumeran bastante bien allí. – BrainSlugs83

1

le interesa el grupo de subprocesos está siempre disponible en aplicaciones .NET, independientemente de qué tipo.

El costo de iniciar un hilo del grupo de subprocesos a través de ThreadPool.QueueUserWorkItem no será más que el costo de iniciar su propio subproceso, y podría ser menor.

Si solo desea algunos hilos durante un período corto, utilice el grupo de subprocesos. Para eso es para eso.

2

Si sus tareas son cortos es muy probable que ver mucho mejor rendimiento mediante la programación de tareas en el grupo de subprocesos a través de QueueUserWorkItem o BeginInvoke y cuando se evite el costo repetida de crear y destruir sus propios hilos.

El grupo de subprocesos obviamente también paga por crear subprocesos, pero reutiliza los subprocesos, por lo que no paga el costo por tarea.

También es posible que desee echar un vistazo a Threading in C# (ebook gratis).

0

Si está creando sus propios hilos, tiene sentido comenzarlos en el primer uso y luego dejarlos por un tiempo en caso de que los necesite de nuevo: hace que un hilo inactivo espere un evento para que pueda reactivarlo señalizando el evento, y después de un tiempo suficientemente largo, el hilo se activará y saldrá, recuperando los recursos no utilizados.Al mantener una pequeña cantidad de subprocesos inactivos listos para ser reutilizados, se reduce la sobrecarga de la creación y destrucción constante de subprocesos (lo cual es significativo para operaciones suficientemente efímeras).

Pero esto es básicamente lo que la cola de hilos ya hace por usted, por lo que puede usarlo también. Es un problema resuelto.

Un debate similar solía ocurrir en la comunidad C++ sobre cadenas, lo creas o no. ¿Deberíamos cada uno escribir nuestra propia clase de cuerda, o deberíamos usar std::string? La respuesta depende de si desea aprender a escribir una clase de cuerda, o ha terminado con eso y desea seguir inventando algo nuevo.

+0

Para el problema que describe, utilizar el modelo de programación asíncrona es mucho más deseable, y utiliza el iirc ThreadPool, y no consume recursos adicionales durante el tiempo de inactividad. –

14

Iba a hacer un comentario, pero se hizo demasiado largo.
CodemonkeyKing parece haber llegado a un punto importante, aunque no lo suficiente en mi opinión.

Existen muchos criterios que puede utilizar para describir el código. ¿Será utilizado en una aplicación de larga ejecución o no? Aplicación Winforms o no? ¿Es una aplicación de servidor o cliente? biblioteca o exe independiente? etc. etc.

Me parece que si su código se ejecutará en una aplicación independiente y usted tiene control sobre todo el código circundante, entonces puede rodar su propio grupo de subprocesos, iniciar sus propios hilos y medir y administrar el costo alrededor del inicio del hilo, latencia del hilo y el consumo de recursos. O puede usar el QUWI, pero no matará su aplicación de ninguna manera. Eres libre de elegir.

Por otro lado, si su código está empaquetado como una biblioteca que puede usarse en un servidor -en ASP.NET, o tal vez en una aplicación SQL CLR, o un Servicio WCF- entonces es una muy mala idea crear hilos. Necesita usar QUWI o algún otro mecanismo que explote el grupo de subprocesos integrado (como BackgroundWorker). Si se va a usar en aplicaciones del lado del cliente con otras bibliotecas, una vez más, se requiere QUWI. Imagine que todas las bibliotecas que quieran aprovechar las computadoras multinúcleo hagan sus propios hilos. Habría un caos completo en las aplicaciones que usaban más de unas pocas bibliotecas. Hilos desenfrenados, todos compiten por los mismos recursos. Sin coordinación central de #threads vs # procesadores.

buena higiene exige que una biblioteca, si se va a consumir en las aplicaciones de cliente o de servidor de aplicaciones, utiliza el threadpool común, y eso significa que QUWI.

Lo último que se debe tener en cuenta es esto;

Un subproceso gestionado es un subproceso en segundo plano o un subproceso en primer plano. Los subprocesos de fondo son idénticos a los subprocesos de primer plano con una excepción: un subproceso en segundo plano no mantiene en ejecución el entorno de ejecución administrada. Una vez que todos los subprocesos de primer plano se han detenido en un proceso administrado (donde el archivo .exe es un ensamblado administrado), el sistema detiene todos los subprocesos de fondo y se apaga.

Los subprocesos que pertenecen al grupo de subprocesos administrados (es decir, los subprocesos cuya propiedad IsThreadPoolThread es verdadera) son subprocesos de fondo. Todos los hilos que ingresan al entorno de ejecución administrada desde un código no administrado se marcan como hilos de fondo. Todos los hilos generados al crear e iniciar un nuevo objeto Thread son por defecto hilos de primer plano.

Si usa un hilo para controlar una actividad, como una conexión de socket, establezca su propiedad IsBackground en verdadero para que el hilo no impida que su proceso finalice.

del MSDN site.

2

Aquí hay otra ventaja de usar ThreadPool.QueueUserWorkItem.

Tengo una aplicación de winforms que tiene buen oído. Originalmente lo implementé usando

  heartbeat = new Thread(HeartbeatDoWork); 
     heartbeat.Start(); 

Que funciona bien el 98% del tiempo. Sin embargo, cuando la aplicación se cierra, a veces no 'muere' correctamente, es decir, el proceso aún es visible en el administrador de tareas.

En resumen, el latido del corazón publicó un evento y estaba manejando el evento (CAB pub/sub) donde se estaba "atascando".

La solución era fácil, basta con cambiar a usar este

  ThreadPool.QueueUserWorkItem(HeartbeatDoWork); 

Estoy seguro de que hay algunas cosas adicionales que podría hacer cuando la escisión de mi propio hilo y que sería limpiado correctamente, pero esto es más simple, más rápido y más fácil de entender ... el ThreadPool hace todo el trabajo por mí.

+9

quizás debería haber agregado la línea: heartbeat.IsBackground = true; –

0

Una ventaja del uso de Thread.Start() es que puede abortar intencionalmente el hilo más tarde. En el ThreadPool no tiene forma de controlar la ejecución del hilo después de ponerlo en cola.

+0

Tengo entendido que, en general, usar Thread.Abort es una mala idea, ya que puede generar recursos que nunca podrán limpiarse. La única vez que se debe usar es cuando la aplicación se está apagando por la fuerza, debido a un estado de error irrecuperable. – devios1

+0

Es posible cancelar algunas operaciones 'ThreadPool'. Ver [esto] (http://stackoverflow.com/questions/7731610/cancel-threadpool-queueuserworkitem-task). –

0

La mayor parte de mi lectura concuerda con Marcel, la capacidad de controlar el hilo si algo sucede (Abortar ...) es algo que se debe considerar especialmente si se trata de llamadas de terceros sobre las cuales no se tiene control y colgar.

+0

¡Más como un comentario! – Nitesh

Cuestiones relacionadas