увеличение текущих логических потоков / утечка стека - PullRequest
9 голосов
/ 21 февраля 2012

Мониторинг моего приложения .NET в системном мониторе. Я вижу .NET CLR LocksAndThreads / # текущих логических потоков неуклонно растет (в настоящее время 293) с течением времени, что указывает на утечку стека потоков.

Я могу найти много статей, которые говорят мне, что это проблема, но ничего не говорит мне, как найти причину - так с чего мне начать?Может ли Windbg сказать мне, в чем проблема?

Это мой монитор производительности за 3 часа, который говорит, что мои текущие логические потоки - 150:

thread leak

И это выводокна потоков, которое не говорит мне много, потому что я не могу получить доступ к их стекам вызовов - они в основном помечены как [недоступно] или [В спящем режиме, подождите или присоединитесь] |[Внешний код]:

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

Обновление: С тех пор я отследил виновника до System.Timers.Timer.Даже когда этот таймер вызывал пустой метод для каждого события Elapsed, он все равно увеличивал количество логических потоков на неопределенное время.Простое изменение таймера на DispatcherTimer устранило проблему.

Я начал изучать все таймеры в своем приложении, увидев большое число при запуске !dumpheap -type TimerCallback в Windbg, как упоминалось в этот вопрос .

Я все еще хотел бы знать, как я мог бы обнаружить это с помощью отладки Windbg, а не путем отключения таймеров / проверки производительности / повторения, которые привели меня к исправлению.То есть все, что могло бы сказать мне, какой таймер создавал проблему.

Ответы [ 2 ]

4 голосов
/ 21 февраля 2012

Обычно это происходит из-за того, что потоки пула потоков зависают и не завершаются. Каждые полсекунды диспетчер пула потоков позволяет другому потоку начинать пытаться устранить отставание. Это продолжается до тех пор, пока не будет достигнуто максимальное количество потоков, установленное ThreadPool.SetMaxThreads (). По умолчанию огромное количество, 1000 на 4-ядерном компьютере.

Используйте Debug + Windows + Threads для просмотра запущенных потоков. Их стек вызовов должен показывать, почему они блокируются.

0 голосов
/ 09 апреля 2013

Попробуйте все ваши длительные операции (более 100 мс, вызовы базы данных, доступ к диску или сети) для асинхронного запуска.

Используйте примитивные инструкции async / await в .NET 4.5.

Пул потоков будет увеличиваться по номеру потока, если поток не доступен, когда задача из очереди извлекается из очереди пула потоков. Если на сервере такая тенденция продолжится, вы, вероятно, закончите голоданием пула потоков. Когда очередь пула потоков заполнена задачами, .net будет отклонять больше запросов, поэтому вы будете на пределе масштабируемости вашего приложения.

Инструкция await создаст рабочий процесс в вашем приложении, освобождая основной поток. После выполнения длительной операции новая задача ставится в очередь в пуле потоков, автоматически позволяя приложению возобновить работу. Освобождение и переработка потоков таким образом будет поддерживать минимальное количество текущих логических потоков, предотвращая голодание и большее количество переключений контекста между потоками.

Также в .NET 4.5 новый алгоритм контролирует затраты / выгоды от создания новых потоков в пуле потоков, сохраняя разумную связь между повышением производительности и переключением контекста, когда существует тенденция к увеличению. Это дополнительное преимущество, которое вы получите, если перейдете на 4,5, если вы еще этого не сделали.

Итак, первый шаг - определить ваши длительные операции и затем сделать их асинхронными.

Вы можете проверить это, сопоставив # текущих логических потоков с другими счетчиками (соединения с клиентом базы данных, чтения с дискового ввода-вывода и т. Д.). Если первое увеличивается, когда другие увеличиваются, вы, вероятно, будете уверены, что это проблема. Также проверьте, сколько времени занимает операция. 100 мс - это хорошая мера, чтобы сказать, что ваша операция в общем смысле длительная.

Надеюсь, эта помощь.

...