线程没有垃圾收集/线程池线程/ C#/ .net

问题描述:

在我的C#/ .net 3.5程序中,我使用线程池线程(委托+ BeginInvoke/EndInvoke)并行化和加快一些文件加载​​。 SystemInternals工具ProcessExplorer显示进程中的线程数量随时间增加,而我期望保持不变。看起来像一些线程/线程处理呆在无处不在。有趣的是,我无法找到线程如何增长,似乎偶尔发生,每次启动应用程序时都没有可重复的模式。我花了一些时间来分析和这里有一些意见:线程没有垃圾收集/线程池线程/ C#/ .net

1)代码如下所示:

ArrayList IAsyncResult_s = new ArrayList(); 
    AsyncProcessing thread1 = processRasterLayer; 
... ArrayList filesToRender.... 
foreach (string FileName in filesToRender) 
    { 
     string fileName2 = FileName; 
     GeoImage partialImage1; 
     IAsyncResult asyncResult = thread1.BeginInvoke(
           fileName2, ....., 
           out partialImage1, ..., null, null); 
     IAsyncResult_s.Add(asyncResult); 
     asyncResult = null; 
    } 
................. 
//block and render all 
foreach (IAsyncResult asyncResult in IAsyncResult_s) 
{ 
    GeoImage partialImage1; 
    thread1.EndInvoke(
    out partialImage1, , asyncResult); 
    //render image.. some calls to render partial image here 
    partialImage1.Dispose(); 
    partialImage1 = null; 
} 
IAsyncResult_s.Clear(); 
IAsyncResult_s = null; 
thread1 = null; 

2)处理线程数 我的跟踪表明,内循环执行过程中,ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads);给数字如493和1000. 循环结束时,ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads);给出数字500,1000。因此,可用的线程数返回到相同的数 报告的进程线程数SystemInternals ProcessExplorer和API System.Diagnostics.Process.GetCurrentProcess() .Threads.Count是循环前16次,循环后大约21次。 如果我调用那些循环,正在处理的线程数会增加,但不是每次修复nubmer,但是每当我重复上面的代码时增加1到4,所以像16-> 21-> 22-> 26-> 31 ...

3)强制垃圾收集没有htelp 我试图去掉垃圾回收来摆脱那些额外的线程,但是这并没有将它们从进程中移除。

4)配置工具 我使用的是RedGates内存和性能分析器,但没有找到明显的原因。我看到几个额外的threas和它们的对象(ThreadContext等)挂起,但没有看到在内存中保存这些线程的对象。我很确定那些额外的线程涉及到循环工作上面,因为我在调用中添加了线程名称,并且他们仍然有我给他们的名字。

5)Intelitrace Intelitrace调试也显示额外的线程挂起。他们仍然有我给他们的名字。但有趣的是,它也显示了现在挂起的同一个线程,在过去的循环中被使用过,而且同一个线程正在执行一些与定时器相关的evens,这些evens来自我的代码的定时器。

6)定位问题 所以,当我在上面禁用循环这个过程filse Asynchroniously,并加载文件sequentialy,我也没有额外的线程,并在我的应用程序线程的数量是恒定的,并和周围16

7)关于SetMaxThreads:它在这里的样子我的机器(XP,.NET 3.5)上:

这样的代码:

ThreadPool.GetAvailableThreads(出AvailableWorkerThreads,出AvailableCompletionPortThreads);
ThreadPool.GetMaxThreads(out MaxWorkerThreads,out MaxCompletionPortThreads);
ThreadPool。GetMinThreads(输出MinWorkerThreads,输出MinCompletionPortThreads);

给出结果:

MinWorkerThreads:2个MaxWorkerThreads:500个MinCompletionPortThreads:2个MaxCompletionPortThreads:1000个AvailableWorkerThreads:500个AvailableCompletionPortThreads:1000

我的应用程序被同时使用也许8个工作线程。我发现SetMaxThreads没有问题。

8) 从功能上来说,到目前为止,在上面的解决方案中我没有问题。但不知何故,如果工具报告我的应用程序中的线程数量正在增加,它看起来像某种“资源泄漏”,我想解决它。它看起来像一些线程手柄无故悬挂。

9)下面是一篇有趣的文章。调用EndInvoke后,清理线程资源变得很简单。我在我的代码中这样做。文章sasy:..“。由于EndInvoke在派生线程后清除,所以必须确保为每个BeginInvoke调用EndInvoke。“”如果线程池线程已退出,EndInvoke将执行以下操作:清除退出线程的松散结束并处理其资源“见:http://en.csharp-online.net/Asynchronous_Programming%E2%80%94BeginInvoke_EndInvoke

10)另一个有趣的文章。作者说他有线程处理泄漏,因为他从非gui线程创建控件。这是相当复杂的文章,请参阅:http://msmvps.com/blogs/senthil/archive/2008/05/29/the-case-of-the-leaking-thread-handles.aspx

11)另一个有趣的文章。它讲述了ThreadPool.SetMinThreads属性。看起来它不是ThreadPool.SetMaxThreads,而是ThreadPool.SetMinThreads,它可以对ThradPool进行有用的控制。这篇文章让我大开眼界,让我想到了ThreadPool的工作原理和性能问题。文章是:http://www.dotnetperls.com/threadpool-setminthreads。另一个类似的是:http://www.codeproject.com/Articles/3813/NET-s-ThreadPool-Class-Behind-The-Scenes

12)另一个有趣的文章。它正在讨论ThreadPool的限制问题。文章提到每秒增加2个新线程的ThreadPool限制。请参阅http_://社交。 MSDN。微软。 com/forums/en-US/clr/thread/3325cb32-371b-4f3e-965f-6ca88538dc3e/

13)所以,在30次测试中,我看到只有2次分配的线程数会缩小。但是,它确实发生了。我看到一次线程号码变成16 - > ....-> 31-> 61-> - > 30-> 16。因此,它回到了16.它不会经常发生,也不是等待时间,它像是在进行中的大型活动,随后是一段持续低水平的活动。

14)ThreadPool.SetMinThreads方法文档。它讨论了每个线程池2个新线程的限制。目前尚不清楚设置此属性是否会消除该限制。 HTTP_://msdn.microsoft。 COM/EN-CA /库/系统。 threading.threadpool.setminthreads(v = vs.90).aspx

+0

您是否管理每个线程上的大对象? – 2013-03-17 09:09:57

+0

这是什么问题呈现? – 2013-03-17 09:38:19

+0

我可能是错的..但AFAIK'线程'不是由GC管理的东西。线程由ThreadPool管理。当你对它做更多的工作时,ThreadPool会创建更多的线程。而且随着工作负载的降低,它决定是否释放一些线程,或者继续保留它们以便将来可能会投入的更多工作。我会说你在工作负载完成后给.NET一些时间(比如5-7分钟),然后再次对线程进行计数。到那个时候,如果你(任何其他你使用的库)没有安排更多的工作,.NET会决定让这些线程去。但我可能是错的。 – Madushan 2013-03-17 09:54:42

所以答案是:这里没有泄漏。这就是线程池的工作原理。它围绕着完成工作的线程,因此在下次需要线程创建时不必付出线程创建的代价。如果您有许多并发工作项,那么池中的线程数会增加,但在MaxWorkerThreads中它们将最大。 (和它无关的垃圾收集器。)

更多信息,请参阅这篇文章: http://msdn.microsoft.com/en-us/library/0ka9477y.aspx

+0

我在上面的文章中添加了一些关于SetMaxThreads的信息。 – 2013-03-17 15:35:23

+0

我不明白为什么它会在我的申请过程中“保留线索”。如果我可能会使用6-12个并行线程,并且在它们完成后,我会在每次处理中获得1-4个额外的线程。线程数量正在从16 ..增加到41. – 2013-03-17 20:13:58

我会考虑消费者生产模式。线程池背后的想法是回收线程,而不是创建数百个新线程。在最好的情况下,你为每个CPU有一个线程,并排队工作。当你避免无用的上下文切换并等待创建新线程时,这一点肯定会更快,就我记得网络线程池等待大约一秒钟,直到创建一个新线程,以使其他线程有机会得到回收。

+0

附加点,您使用异步代码,它也为回调添加线程。 – NickD 2013-03-22 06:34:51