1

我製作了一個軟件來處理圖像。它試圖在圖像中查找圖案,並且如果它找到了它正在查找的圖案,它會將圖像名稱,圖案類型和座標寫入文本文件中。使用GC.Collect預防OutOfMemoryException()

正如您可能知道的那樣,圖像處理需要很長時間。所以我決定使用多線程來提高性能。

bool Multithread = CheckMultithread(); 
UpdateParameters(); 

if (Multithread) 
{ 
    Parallel.For(0, FileNames.Length ,i => Solve(FileNames[i])); 
} 
else 
{ 
    foreach (string s in FileNames) 
    { 
     Solve(s); 
    } 
} 

這是我第一次嘗試在C#中使用多線程。但是我沒有多線程相關的錯誤,因爲處理過程不會干擾另一個圖像的處理。

問題是:如果多線程處於ON狀態,當我到達第200個左右的圖像時,會出現OutOfMemoryException ...顯然,這種並行性(實際上整個代碼在不同線程中運行)消耗N倍以上的內存(N ==正在運行的線程數)...

我修改了我創建的每個類,並且只有一個使用非託管代碼(QuickBitmap.cs,Bitmap類的封裝器,使用lockbits和unlockbits來提高表現)。 但是我已經在該類中實現了IDisposable接口,所以...我不知道爲什麼會發生這種情況。

每個線程都需要+/- 400 mb的RAM來運行。 當拋出OutOfMemoryException時,程序使用大約1300 mb的內存。甚至儘管我有超過9 GB的可用內存

爲了解決這個例外,我添加以下代碼,就在乞討解決()

 if (GC.GetTotalMemory(false) > 1000*1000*1000) 
     { 
      lock (Manager.dasLock) 
      { 
       Manager.sw.Start(); 
       GC.Collect(); 
       Manager.sw.Stop(); 
      } 
     } 

ProcessedCount裏面遞增靜態INT一個lock()語句。

添加那段代碼後,程序可以處理所有圖像(2000+)而不會拋出任何異常。我也從來沒有使用超過1.5 GB的內存。

但是,既然每個人都在爲讓我爲DARING打電話給全能的GC ... 我該怎麼做才能防止這種情況?

我可以發佈更詳細的代碼,如果你們想。 Ps:在Solve()的最後一行中,我調用每個對象的非託管資源的Dispose,並將所有的vars設置爲null。

PPS:是的,這是一個32位的軟件,但限制應該是4 GB而不是1.5,對吧?

Edit3:更改GC.Collect調用代碼。這樣我就可以瞭解我花了多少時間來收藏。對於每一分鐘處理,我都花費0.4s收集。

+0

你試圖保存處理圖像並保存後手動處置他們?而1,3GB內存上限是因爲32位;) – 2014-09-03 12:10:08

+0

你假定Parallel.For爲每次迭代創建一個線程是不正確的。它將集合分成幾個組,每個組在一個線程中運行。 – Dirk 2014-09-03 12:10:59

+0

可以肯定的是,因爲我發現你最後的段落會讓人困惑:如果你調用'GC.Collect',會導致一個'OutOfMemoryException',但是如果沒有它,即使使用'Parallel.For'循環,一切都可以正常運行? – Dirk 2014-09-03 12:14:38

回答

1

限制線程數來解決這個問題:

 if (Multithread) 
     { 
      ParallelOptions pOptions = new ParallelOptions(); 
      pOptions.MaxDegreeOfParallelism = Environment.ProcessorCount; 
      Parallel.For(0, FileNames.Length, pOptions, i => Solve(FileNames[i])); 
     } 
     else 
     { 
      foreach (string s in FileNames) 
      { 
       Solve(s); 
      } 
     } 
0

儘管沒有說明,但如果您正在進行圖像處理,您正在處理的某些對象很可能會實現IDisposable接口 - 例如File或Image對象。

對於所有這些對象,您應該嘗試使用「使用」塊(See here)。

當你調用GC.Collect時,很可能會拋棄這些對象,這就是爲什麼我從你理解的GC中調用GC的原因。收集你不會例外

+0

我知道。但正如我所說,我正在調用Dispose來處理每個這樣的對象。 – Trauer 2014-09-03 17:26:29

+0

你說你在求解結束時調用它。你有沒有嘗試在那裏放置一個斷點來確認你到達最後一行? – Zeus82 2014-09-03 18:40:44