2012-06-08 51 views
3

我正在一個網站上工作,我需要能夠將4000x6000分割成4個部分(其中包括許多其他任務),並且我需要儘可能快地爲多個用戶。並行運行多個圖像操作導致OutOfMemory異常

我這樣做當前的代碼

var bitmaps = new RenderTargetBitmap[elements.Length]; 

using (var stream = blobService.Stream(key)) 
{ 
    BitmapImage bi = new BitmapImage(); 
    bi.BeginInit(); 
    bi.StreamSource = stream; 
    bi.EndInit(); 

    for (var i = 0; i < elements.Length; i++) 
    { 
     var element = elements[i]; 

     TransformGroup transformGroup = new TransformGroup(); 
     TranslateTransform translateTransform = new TranslateTransform(); 
     translateTransform.X = -element.Left; 
     translateTransform.Y = -element.Top; 
     transformGroup.Children.Add(translateTransform); 

     DrawingVisual vis = new DrawingVisual(); 
     DrawingContext cont = vis.RenderOpen(); 
     cont.PushTransform(transformGroup); 
     cont.DrawImage(bi, new Rect(new Size(bi.PixelWidth, bi.PixelHeight))); 
     cont.Close(); 

     RenderTargetBitmap rtb = new RenderTargetBitmap(element.Width, element.Height, 96d, 96d, PixelFormats.Default); 
     rtb.Render(vis); 
     bitmaps[i] = rtb; 
    } 
} 

for (var i = 0; i < bitmaps.Length; i++) 
{ 
    using (MemoryStream ms = new MemoryStream()) 
    { 
     PngBitmapEncoder encoder = new PngBitmapEncoder(); 
     encoder.Frames.Add(BitmapFrame.Create(bitmaps[i])); 
     encoder.Save(ms); 
     var regionKey = WebPath.Variant(key, elements[i].Id); 
     saveBlobService.Save("image/png", regionKey, ms); 
    } 
} 

我運行它採取的工作掀起了隊列多個線程。我發現如果這部分代碼一次被4個線程命中,我會得到一個OutOfMemory異常。我可以通過將上面的所有代碼包裝在lock(obj)中來阻止這種情況的發生,但這並不理想。我試過包裝第一個使用塊(從磁盤讀取文件並拆分),但仍然出現內存不足異常(這部分代碼執行得相當快)。

  • 我這個正常考慮這個應該佔用的內存量嗎?
  • 我可以做什麼優化?
  • 我可以增加可用內存嗎?

UPDATE:

我的新代碼,每Moozhe的幫助

public static void GenerateRegions(this IBlobService blobService, string key, Element[] elements) 
{ 
    using (var stream = blobService.Stream(key)) 
    { 
     foreach (var element in elements) 
     { 
      stream.Position = 0; 
      BitmapImage bi = new BitmapImage(); 
      bi.BeginInit(); 
      bi.SourceRect = new Int32Rect(element.Left, element.Top, element.Width, element.Height); 
      bi.StreamSource = stream; 
      bi.EndInit(); 

      DrawingVisual vis = new DrawingVisual(); 
      DrawingContext cont = vis.RenderOpen(); 
      cont.DrawImage(bi, new Rect(new Size(element.Width, element.Height))); 
      cont.Close(); 

      RenderTargetBitmap rtb = new RenderTargetBitmap(element.Width, element.Height, 96d, 96d, PixelFormats.Default); 
      rtb.Render(vis); 

      using (MemoryStream ms = new MemoryStream()) 
      { 
       PngBitmapEncoder encoder = new PngBitmapEncoder(); 
       encoder.Frames.Add(BitmapFrame.Create(rtb)); 
       encoder.Save(ms); 
       var regionKey = WebPath.Variant(key, element.Id); 
       blobService.Save("image/png", regionKey, ms); 
      } 
     } 
    } 
} 
+0

您的服務器上有多少RAM?也許重試和隊列機制(例如排隊請求)可以在這裏幫助。 – ashes999

+0

@ ashes999現在通過iis express在我的本地機器上運行,我的機器有12gigs – Tom

+1

您是否嘗試編寫一些測試代碼,在您的計算機上加載10個或更多圖像以查看加載圖像是否導致問題? – Ani

回答

3

如果你想打電話並行4000x6000圖像的DrawImage,你將有一個壞的時間。當你渲染到RenderTargetBitmap的時候,你已經裁剪得太晚了,它已經在內存中渲染完整大小了。

而是與變換,嘗試使用BitmapImage.SourceRect屬性像這樣裁剪圖像源的:

BitmapImage.SourceRect =新的Rect(element.Left,element.Top,element.Width,元件。高度);

你可能想在你調用BeginInit()之前試着把它完全拋棄掉。

編輯:其實在你的情況下,你必須改變for循環的每次迭代SourceRect。請記住,您必須將DrawImage中的Size參數更改爲元素大小。

+0

我喜歡這樣的聲音,現在要放棄了:) – Tom

+0

已更新我的代碼,並張貼上面,我發現SourceRect似乎沒有影響輸出雖然,是讓整個圖像返回調整大小不同的形狀? – Tom

+0

這聽起來不對。也許嘗試一個CroppedBitmap而不是BitmapImage?它應該與SourceRect一起工作。 –

相關問題