2012-02-21 9 views
8

Monitorowanie mojej aplikacji .NET w Monitorze wydajności Widzę .NET CLR LocksAndThreads/# bieżących wątków logicznych zwiększa się stale (obecnie 293) w czasie, co wskazuje na stos wątków przecieka.bieżące wątki zwiększające logikę/stos wątków wycieka

Mogę znaleźć wiele artykułów, które mówią mi, że to jest problem, ale nic, co mówi mi, jak znaleźć przyczynę - więc od czego mam zacząć? Czy Windbg może mi powiedzieć, gdzie leży problem?

To jest mój monitor wydajności ponad 3 godziny opowiadać bieżące wątki logiczne jest 150:

thread leak

I to jest wyjście z okna wątków, które nie powiedzieć wiele, ponieważ nie mogę uzyskać dostęp do swoich stosów połączeń - są one najczęściej oznaczone jako [niedostępny] lub [we śnie, czekaj lub dołącz] [Kod zewnętrzny]:

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 

Aktualizacja: Mam ponieważ śledzone sprawcę dół do System.Timers.Timer. Nawet jeśli ten timer nazywał pustą metodę na każdym zdarzeniu, które upłynęło, ciągle zwiększał liczbę wątków logicznych w nieskończoność. Po prostu zmiana timera na DispatcherTimer naprawiła problem.

Zacząłem sprawdzać wszystkie liczniki czasu w mojej aplikacji po zobaczeniu dużej liczby przy uruchamianiu !dumpheap -type TimerCallback w Windbg, jak wspomniano w this question.

Nadal chciałbym wiedzieć, w jaki sposób mogłem wykryć to poprzez debugowanie Windbg, a nie wyłączyć timers/sprawdź metodę wydajności/powtórzenia, która doprowadziła mnie do poprawki. To znaczy. wszystko, co mogłoby mi powiedzieć, który zegar tworzy problem.

+0

Czy wiesz, co je tworzy i dlaczego? –

+0

Moja aplikacja ma wiele ruchomych części, więc "dlaczego" to wiele różnych zadań wykonywanych w tle. Próbuję znaleźć źródło wzrostu, aby dowiedzieć się "co". – DaveO

Odpowiedz

4

Jest to zwykle spowodowane zablokowaniem wątków puli nici i ich niezakończeniem. Co pół sekundy menedżer wątków pozwala, aby inny wątek zaczął próbować zlikwidować zaległości. To trwa, dopóki nie osiągnie maksymalnej liczby wątków ustawionych przez ThreadPool.SetMaxThreads(). Domyślnie ogromna liczba, 1000 na czterordzeniowej maszynie.

Użyj debugowania + Windows + wątki, aby spojrzeć na działające wątki. Ich stos wywołań powinien wyjaśnić, dlaczego blokują.

+0

Cześć Hans. Miałem wygląd, ale jak zaktualizowano powyżej, nie widzę żadnych użytecznych informacji. Czy jest to możliwe z powodu niezarządzanego kodu, dlatego większość wymienionych wątków jest niedostępna? – DaveO

+0

Najwyraźniej mój max. wątki z ThreadPool.GetMaxThreads to 1023, ale obecnie perfmon pokazuje ponad 2400 bieżących wątków logicznych. – DaveO

+0

Hmm, zawsze jest wielokrotnością 250, chyba że zostanie nadpisany. Nie ma to znaczenia, 2400 wątków jest oczywiście daleko poza szczęśliwym punktem i prawdziwym problemem. Posiadanie 1023 nie czyni tego lepszym. –

1

Spróbuj uruchomić wszystkie swoje długie operacje (100+ ms połączenia z bazami danych, dostęp do dysku lub sieci), aby działać asynchronicznie.

Użyj asynchronicznych/oczekujących prostych instrukcji w .NET 4.5.

Pula wątków zwiększy się w numerze wątku, jeśli wątek nie jest dostępny, gdy zadanie kolejkowane jest pobierane z kolejki puli wątków. Jeśli tendencja będzie się utrzymywać w ten sposób na serwerze, prawdopodobnie zakończy się z powodu zagłodzenia puli wątków. Z kolejką puli wątków pełną zadań. Net odrzuci więcej żądań, dzięki czemu osiągniesz maksymalną skalowalność swojej aplikacji.

czekać na instrukcję spowoduje wygenerowanie przepływu pracy w aplikacji, uwalniając główny wątek. Po zakończeniu długotrwałej operacji nowe zadanie jest umieszczane w kolejce w puli wątków, automatycznie umożliwiając wznowienie aplikacji. Uwolnienie i powtórzenie wątków w ten sposób spowoduje, że liczba bieżących wątków logicznych utrzyma się na minimalnym poziomie, zapobiegając głodom i większemu kontekstowi przełączającemu się między wątkami.

Również w .NET 4.5 nowy algorytm kontroluje koszt/korzyść tworzenia nowego wątku wewnątrz puli wątków, utrzymując rozsądną zależność pomiędzy wzrostem wydajności i przełączaniem kontekstu, gdy tendencja ma się zwiększać. Jest to dodatkowa korzyść, którą otrzymasz, jeśli przejdziesz do 4.5, jeśli już tego nie zrobiłeś.

Pierwszym krokiem jest zidentyfikowanie długich operacji, a następnie ich asynchronizacja.

Można to sprawdzić, porównując liczbę bieżących wątków logicznych z innymi licznikami (połączenia klienta bazy danych, odczyty dysku IO itp.). Jeśli pierwszy wzrost nastąpi, gdy inne wzrosną, prawdopodobnie będziesz mieć pewność, że to jest problem. Sprawdź również, jak długo trwają operacje. 100 ms to dobry sposób na stwierdzenie, że twoja operacja długo działa w ogólnym znaczeniu.

Mam nadzieję, że ta pomoc.