当前逻辑线程增加/线程堆栈正在泄漏

时间:2012-02-21 12:23:12

标签: .net multithreading memory-leaks c++-cli

在性能监视器中监视我的.NET应用程序我可以看到 .NET CLR LocksAndThreads /当前逻辑线程数随着时间的推移稳步增加(当前为293),这表明线程堆栈正在泄漏。 / p>

我可以找到很多文章告诉我这是问题,但没有任何东西告诉我如何找到原因 - 那么我从哪里开始? 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就可以解决问题。

我在this question中提到的在Windbg中运行!dumpheap -type TimerCallback后看到一个大数字时,我开始查看我的应用程序中的所有计时器。

我仍然想知道我是如何通过Windbg调试检测到的,而不是通过禁用定时器/检查性能/重复方法来引导我进行修复。即任何可以告诉我哪个计时器正在创造问题的东西。

2 个答案:

答案 0 :(得分:4)

这通常是由线程池线程卡住而未完成引起的。每隔半秒,线程池管理器允许另一个线程开始尝试解决积压问题。这一直持续到它达到ThreadPool.SetMaxThreads()设置的最大线程数。默认情况下,4核机器上的数字很大,为1000。

使用Debug + Windows + Threads查看正在运行的线程。他们的调用堆栈应该明确他们阻塞的原因。

答案 1 :(得分:0)

尝试所有长时间运行的操作(100多天数据库调用,磁盘或网络访问)以异步方式运行。

在.NET 4.5中使用async / await原语指令。

如果从线程池队列中检索到排队的任务时没有线程可用,则线程池的线程数会增加。如果趋势在服务器中以这种方式继续,您可能会以线程池饥饿结束。如果线程池队列中充满了任务.net将拒绝更多请求,因此您将处于应用程序可伸缩性的极限。

await指令将在您的应用程序中生成一个工作流程,释放主线程。在长时间运行操作完成后,新任务将在线程池中排队,自动让您恢复应用程序。以这种方式释放和回收线程将使当前逻辑线程的数量保持在最低水平,从而防止饥饿和线程之间的更多上下文切换。

同样在.NET 4.5中,新算法控制线程池内新线程创建的成本/收益,在趋势增加时保持性能增加和上下文切换之间的合理关系。如果你已经没有这样做,那么你可以获得额外的好处。

因此,第一步是识别长时间运行的操作,然后使它们异步。

您可以通过将当前逻辑线程的#与其他计数器(数据库客户端连接,磁盘IO读取等)相关联来验证这一点。如果第一次增加,当其他人增加时,你可能会确定这是问题所在。还要检查操作需要多长时间。 100毫秒是一个很好的衡量标准,说你的操作在一般意义上是长期运行。

希望得到这个帮助。