2013-03-17 127 views
2

在我的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

回答

3

所以答案是:這裏沒有泄漏。這就是線程池的工作原理。它圍繞着完成工作的線程,因此在下次需要線程創建時不必付出線程創建的代價。如果您有許多併發工作項,那麼池中的線程數會增加,但在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

0

我會考慮消費者生產模式。線程池背後的想法是回收線程,而不是創建數百個新線程。在最好的情況下,你爲每個CPU有一個線程,並排隊工作。當你避免無用的上下文切換並等待創建新線程時,這一點肯定會更快,就我記得網絡線程池等待大約一秒鐘,直到創建一個新線程,以使其他線程有機會得到回收。

+0

附加點,您使用異步代碼,它也爲回調添加線程。 – NickD 2013-03-22 06:34:51