2012-02-21 6 views
8

Supervisando mi aplicación .NET en el Monitor de rendimiento Puedo ver .NET CLR LocksAndThreads/# de subprocesos lógicos actuales está aumentando constantemente (actualmente 293) en el tiempo que indica la pila de subprocesos esta goteando.subprocesos lógicos actuales aumentando/la pila de subprocesos se está filtrando

puedo encontrar muchos artículos que me digas que esto es el problema, pero nada de lo que me dice cómo encontrar la causa - por lo que ¿Por dónde comienzo? ¿Puede Windbg decirme dónde está el problema?

Ésta es mi monitor de rendimiento a lo largo de 3 horas contando mis hilos lógicos actuales es de 150:

thread leak

Y esta es la salida de la ventana de hilos, que no me dice mucho porque no puede acceder a sus llamadas: en su mayoría están marcados como [no disponible] o [En espera, espere o únase] | [Código externo]:

Unflagged  141024 124 Worker Thread <No Name>  Normal 
Unflagged > 0 0 Unknown Thread [Thread Destroyed]  
Unflagged  136272 2 Worker Thread <No Name>  Highest 
Unflagged  133060 7 Worker Thread vshost.RunParkingWindow [Managed to Native Transition] Normal 
Unflagged  136952 10 Main Thread Main Thread [edited].Program.Main Normal 
Unflagged  134544 9 Worker Thread .NET SystemEvents [Managed to Native Transition] Normal 
Unflagged  136556 11 Worker Thread Worker Thread [edited].MessageService.ProcessJobs.AnonymousMethod__0 Normal 
Unflagged  141364 113 Worker Thread <No Name> [In a sleep, wait, or join] Normal 
Unflagged  140896 0 Worker Thread [Thread Destroyed]  Normal 
Unflagged  136776 19 Worker Thread <No Name> [In a sleep, wait, or join] Normal 
Unflagged  135704 20 Worker Thread <No Name> [In a sleep, wait, or join] Normal 
Unflagged  136712 21 Worker Thread <No Name> [In a sleep, wait, or join] Normal 
Unflagged  134984 22 Worker Thread <No Name> [In a sleep, wait, or join] Normal 
Unflagged  134660 23 Worker Thread Worker Thread [edited].BroadcastService.ProcessJobs.AnonymousMethod__1d Normal 
Unflagged  140224 152 Worker Thread <No Name>  Normal 
Unflagged  140792 157 Worker Thread <No Name>  Normal 
Unflagged  137116 0 Worker Thread <No Name>  Normal 
Unflagged  140776 111 Worker Thread <No Name>  Normal 
Unflagged  140784 0 Worker Thread [Thread Destroyed]  Normal 
Unflagged  140068 145 Worker Thread <No Name>  Normal 
Unflagged  139000 150 Worker Thread <No Name>  Normal 
Unflagged  140828 52 Worker Thread <No Name>  Normal 
Unflagged  137752 146 Worker Thread <No Name>  Normal 
Unflagged  140868 151 Worker Thread <No Name>  Normal 
Unflagged  141324 139 Worker Thread <No Name>  Normal 
Unflagged  140168 154 Worker Thread <No Name>  Normal 
Unflagged  141848 0 Worker Thread [Thread Destroyed]  Normal 
Unflagged  135544 153 Worker Thread <No Name>  Normal 
Unflagged  142260 140 Worker Thread <No Name>  Normal 
Unflagged  141528 142 Worker Thread <No Name> [In a sleep, wait, or join] Normal 
Unflagged  141344 0 Worker Thread [Thread Destroyed]  Normal 
Unflagged  140096 136 Worker Thread <No Name>  Normal 
Unflagged  141712 134 Worker Thread <No Name>  Normal 
Unflagged  141688 147 Worker Thread <No Name>  Normal 

Actualización: Desde entonces, he seguido el culpable a una System.Timers.Timer. Incluso cuando este temporizador llamó a un método vacío en cada evento transcurrido, aún aumentó el recuento de hilos lógicos indefinidamente. El simple hecho de cambiar el temporizador a un DispatcherTimer ha solucionado el problema.

Comencé a buscar en todos los temporizadores de mi aplicación después de ver un número grande al ejecutar !dumpheap -type TimerCallback en Windbg como se menciona en this question.

todavía me gustaría saber cómo me podría haber detectado esto a través de WinDbg depuración en lugar del método desactivar los temporizadores/rendimiento/comprobación de repetición que me llevan a la corrección. Es decir. cualquier cosa que podría haberme dicho qué temporizador estaba creando el problema.

+0

¿Sabes qué los está creando y por qué? –

+0

Mi aplicación tiene muchas partes móviles, por lo que el "por qué" sería una cantidad de tareas de fondo distintas. Estoy tratando de encontrar la fuente del aumento para descubrir "qué". – DaveO

Respuesta

4

Esto suele deberse a que los hilos del grupo de hilos se atascan y no se completan. Cada medio segundo, el administrador de subprocesos de subprocesos permite que otro subproceso comience a tratar de trabajar en la acumulación. Esto continúa hasta alcanzar la cantidad máxima de subprocesos configurados por ThreadPool.SetMaxThreads(). Por defecto, un número enorme, 1000 en una máquina de 4 núcleos.

Use Debug + Windows + Threads para ver los subprocesos en ejecución. Su pila de llamadas debería hacer obvio por qué están bloqueando.

+0

Hola Hans. He echado un vistazo, pero como se actualizó anteriormente, realmente no puedo ver ninguna información útil. ¿Es posible que sea causado por un código no administrado, que es la razón por la cual la mayoría de los hilos enumerados no están disponibles? – DaveO

+0

Aparentemente mi max. hilos de ThreadPool.GetMaxThreads es 1023, pero perfmon actualmente muestra más de 2400 hilos lógicos actuales .. – DaveO

+0

Hmm, siempre es un múltiplo de 250 a menos que se anule expresamente. Poco importa, 2400 hilos es, por supuesto, más allá del punto feliz y el problema real. Tener 1023 no lo hace mejor. –

1

Pruebe todas sus operaciones de larga ejecución (llamadas de base de datos de más de 100 ms, disco o acceso a la red) para ejecutar de forma asíncrona.

Utilice las instrucciones primitivas async/await en .NET 4.5.

conjunto de subprocesos se incrementará en número hilo si hay hilo está disponible cuando una tarea en cola se recupera de la cola de grupo de subprocesos. Si la tendencia continúa de esta manera en el servidor, probablemente terminará con un hacinamiento en el grupo de subprocesos. Con la cola del grupo de subprocesos llena de tareas, .net rechazará más solicitudes, por lo que estará al límite de la escalabilidad de su aplicación.

instrucciones de espera generará un flujo de trabajo en su aplicación, liberando el hilo principal. Una vez que finaliza la operación de larga ejecución, se pone en cola una nueva tarea en el grupo de subprocesos que permite automáticamente que la aplicación se reanude. Al liberar y reciclar subprocesos de esta manera, se mantendrá el n. ° de subprocesos lógicos actuales en un nivel mínimo, evitando la inanición y más cambios de contexto entre subprocesos.

También en .NET 4.5 un nuevo algoritmo controla el costo/beneficio de la nueva creación de subprocesos dentro del grupo de subprocesos, manteniendo una relación razonable entre el aumento de rendimiento y el cambio de contexto cuando la tendencia es a aumentar. Este es un beneficio adicional que obtiene si pasa a 4.5 si ya no lo ha hecho.

De modo que el primer paso es identificar las operaciones de larga ejecución y luego hacerlas asincrónicas.

Puede verificar esto correlacionando # de subprocesos lógicos actuales con otros contadores (conexiones de cliente de base de datos, lecturas de IO de disco, etc.). Si el primero aumenta cuando los otros aumentan, es probable que esté seguro de que este es el problema. También verifique cuánto tiempo tardan las operaciones. 100 ms es una buena medida para decir que su operación es de larga duración en un sentido general.

Espero que esta ayuda.

Cuestiones relacionadas