2012-02-21 22 views
8

Monitoraggio della mia app .NET in Performance Monitor Sono in grado di vedere i .NET CLR LocksAndThreads/# dei thread logici correnti aumenta costantemente (attualmente 293) nel tempo che indica lo stack di thread sta perdendo.thread logici correnti in aumento/perdita di thread in uscita

Sono in grado di trovare molti articoli che mi dicono che questo è il problema ma nulla che mi dica come trovare la causa - quindi da dove comincio? Windbg può dirmi dove si trova il problema?

Questo è il mio monitor delle prestazioni oltre 3 ore a dire ai miei attuali discussioni logici è di 150:

thread leak

E questo è l'output della finestra di discussioni, che non mi dice molto perché non posso accedi ai loro stack di chiamate - sono per lo più contrassegnati come [non disponibile] o [In sleep, wait or join] | [Codice esterno]:

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 

Aggiornamento: allora ho rintracciato il colpevole fino a uno System.Timers.Timer. Anche quando questo timer ha chiamato un metodo vuoto su ogni evento Elapsed, ha comunque aumentato il conteggio thread logico per un tempo indefinito. Il solo cambio del timer su DispatcherTimer ha risolto il problema.

Ho iniziato a esaminare tutti i timer nella mia applicazione dopo aver visto un numero elevato quando si eseguiva !dumpheap -type TimerCallback in Windbg come indicato in this question.

Vorrei ancora sapere come ho potuto rilevare questo tramite il debug di Windbg piuttosto che i timer di disabilitazione/controllo delle prestazioni/metodo di ripetizione che mi portano alla correzione. Cioè tutto ciò che avrebbe potuto dirmi quale timer stava creando il problema.

+0

Sai cosa li sta creando e perché? –

+0

La mia app ha molte parti in movimento, quindi il "perché" sarebbe un numero di attività in background diverse. Sto cercando di trovare la fonte dell'aumento per capire "cosa". – DaveO

risposta

4

Ciò è in genere causato dall'avere thread bloccati e non completati. Ogni mezzo secondo, il gestore del threadpool consente a un altro thread di iniziare a cercare di ridurre il backlog. Questo continua fino a raggiungere il numero massimo di thread come impostato da ThreadPool.SetMaxThreads(). Di default un numero enorme, 1000 su una macchina a 4 core.

Utilizzare Debug + Windows + Thread per esaminare i thread in esecuzione. Il loro stack di chiamate dovrebbe rendere evidente il motivo per cui stanno bloccando.

+0

Ciao Hans. Ho avuto un aspetto ma come aggiornato sopra non riesco davvero a vedere alcuna informazione utile. È possibile che sia causato da codice non gestito, motivo per cui la maggior parte dei thread elencati non sono disponibili? – DaveO

+0

Apparentemente il mio max. thread da ThreadPool.GetMaxThreads è 1023, ma perfmon sta attualmente mostrando oltre 2400 thread logici correnti. – DaveO

+0

Hmm, è sempre un multiplo di 250 se non espressamente ignorato. Difficilmente conta, 2400 thread è ovviamente ben oltre il punto felice e il vero problema. Avere 1023 non lo rende migliore. –

1

Provare tutte le operazioni di lunga durata (100+ ms chiamate di database, disco o accesso alla rete) per eseguire in modo asincrono.

Utilizzare istruzioni primarie asincrone/attese in .NET 4.5.

Il pool di thread aumenterà il numero di thread se nessun thread è disponibile quando un'attività in coda viene recuperata dalla coda del pool di thread. Se la tendenza continua in questo modo nel server, probabilmente finirai con l'inedia del pool di thread. Con la coda del pool di thread piena di attività .net rifiuterà più richieste, così sarai al limite della scalabilità della tua applicazione.

Attendere l'istruzione genererà un flusso di lavoro nell'applicazione, liberando il thread principale. Una volta eseguita l'operazione di lunga durata, nel pool di thread viene automaticamente accodata una nuova attività che consente di riprendere l'applicazione. Liberare e riciclare i thread in questo modo manterrà il numero di thread logici attuali a un livello minimo, impedendo la fame e più opzioni di contesto tra i thread.

Anche in .NET 4.5 un nuovo algoritmo controlla il rapporto costi/benefici della creazione di nuovi thread all'interno del pool di thread, mantenendo una relazione ragionevole tra aumento delle prestazioni e cambio di contesto quando la tendenza è di aumentare. Questo è un vantaggio aggiuntivo che ottieni se passi a 4.5 se già non lo hai fatto.

Quindi il primo passo è identificare le operazioni a lungo termine e quindi renderle asincrone.

È possibile verificare questo correlando il numero di thread logici correnti con altri contatori (connessioni client del database, letture IO del disco, ecc.). Se il primo aumento quando aumentano gli altri, è probabile che questo sia il problema. Controlla anche per quanto tempo durano le operazioni. 100 ms è una buona misura per dire che l'operazione è di lunga durata in senso generale.

Spero che questo aiuto.