title: Multi-threaded image rotation in .net
body: <p>Sorry kinda frustrated, so asking for some help. And yeah similar questions have been asked before but I didn't fina way to resolve this.</p>
  
  <p>I am getting a System.OutOfMemoryException when trying to read a group of images and rotate them. Essentially, I have a large number of images. I want to read each image into memory and then rotate the image 90, 180, and 270 degrees and save each rotated image out as a file.</p>
  
  <p>I do not have corrupted images. If I read each image individually and rotate them one at a time everything works fine. All images to rotate exist in the same directory. The directory can hold up to several hundred images. Each individual image is in itself not that large: average size &lt; 1 mb stored as a .PNG file. All images are saved as PNG files. </p>
  
  <p>In addition, I have a server that I am running on. It is a dual CPU machine. Each CPU has 8 processors with hyperthreading. That makes 32 processors/threads. This machine has 64gb of ram. I want to use these resources to process the images in parallel using .net 4.5 and I cannot do so and can't figure out why. I SHOULD be able to process many images in parallel and not run out of memory ... but can't !!!</p>
  
  <p>Here is the routine that rotates the images:</p>
  
  <pre><code>Private Sub RotateImage90_180_270(ByVal filename As String)
  
      Dim file90 As String = filename.Replace(".png", "_rot90.png")
      Dim file180 As String = filename.Replace(".png", "_rot180.png")
      Dim file270 As String = filename.Replace(".png", "_rot270.png")
  
      Using fs As New FileStream(filename, FileMode.Open, FileAccess.Read)
          Using original As Image = Image.FromStream(fs)
  
              original.RotateFlip(RotateFlipType.Rotate90FlipNone)
              original.Save(file90, System.Drawing.Imaging.ImageFormat.Png)
  
              original.RotateFlip(RotateFlipType.Rotate90FlipNone)
              original.Save(file180, System.Drawing.Imaging.ImageFormat.Png)
  
              original.RotateFlip(RotateFlipType.Rotate90FlipNone)
              original.Save(file270, System.Drawing.Imaging.ImageFormat.Png)
  
          End Using
      End Using
  
  End Sub
  </code></pre>
  
  <p>The below is the routine that works; if sequential. Get a list of files in the folder. Iterate 1 file at a time thru the list generating the rotation files. 31 of my 32 cpus sit idle and I wait many minutes waiting for the process to finish.</p>
  
  <pre><code>Private Sub doRotations(ByVal folderPath As String)
  
      Dim files As String()
  
  
      files = System.IO.Directory.GetFiles(folderPath, "*.png")
  
      Me.EventLog1.WriteEntry("doStep4 PATH: " &amp; folderPath &amp; "  FILE COUNT: " &amp; files.Length)
  
      For i = 0 To files.Length - 1
  
          Try
              RotateImage90_180_270(files(i))
          Catch ex As Exception
              Me.EventLog1.WriteEntry("doStep4 ERROR: " &amp; ex.ToString &amp; "File: " &amp; files(i), EventLogEntryType.Error)
          End Try
  
      Next
  
  End Sub
  </code></pre>
  
  <p>So this is where my code starts to fail. Parallel for loop, kicking off a thread to process each image separately.</p>
  
  <pre><code>Private Sub doRotations_InParallel(ByVal folderPath As String)
  
      Dim files As String()
  
      files = System.IO.Directory.GetFiles(folderPath, "*.png")
  
      Me.EventLog1.WriteEntry("doStep4 PATH: " &amp; folderPath &amp; "  FILE COUNT: " &amp; files.Length)
  
      Parallel.ForEach(files, Sub(currentFile)
                                  RotateImage90_180_270(currentFile)
                              End Sub)
  End Sub
  </code></pre>
  
  <p>I have also tried breaking the entire list of files in the directory into a set of files and running each set in parallel. For example run 25 at a time in parallel, then the next 25, and so on. Since I have 32 CPUs and using 25 threads I should not have a problem. But I do !!!</p>
  
  <p>Since the routine that does the rotations has "Using" blocks, shouldn't the file handles and allocated memory automatically be freed ? And since each is on its own thread from the .Foreach shouldn't the memory spaces be separate (why would memory allocated on one thread affect a separate thread ?). </p>
  
  <p>Anyway, help would be appreciated !!! Including options other than using System.Drawing.Imaging since I believe that the memory leak is coming from there.</p>
  
  <p>Oh and I just tried the code below and its won't work. Tried getting a copy of the image read in and manually closing/disposing before rotating/saving. Still doesn't work. There is obviously a memory leak coming from somewhere; just can't figure out where. </p>
  
  <p>Please help !</p>
  
  <pre><code>Private Sub RotateImage90_180_270(ByVal filename As String)
  
      Dim file90 As String = filename.Replace(".png", "_rot90.png")
      Dim file180 As String = filename.Replace(".png", "_rot180.png")
      Dim file270 As String = filename.Replace(".png", "_rot270.png")
  
      Dim originalCopy As Image
  
      Using fs As New FileStream(filename, FileMode.Open, FileAccess.Read)
          Using original As Image = Image.FromStream(fs)
  
              originalCopy = original.Clone
              'original.RotateFlip(RotateFlipType.Rotate90FlipNone)
              'original.Save(file90, System.Drawing.Imaging.ImageFormat.Png)
  
              'original.RotateFlip(RotateFlipType.Rotate90FlipNone)
              'original.Save(file180, System.Drawing.Imaging.ImageFormat.Png)
  
              'original.RotateFlip(RotateFlipType.Rotate90FlipNone)
              'original.Save(file270, System.Drawing.Imaging.ImageFormat.Png)
  
              original.Dispose()
  
          End Using
  
          fs.Close()
          fs.Dispose()
  
      End Using
  
      originalCopy.RotateFlip(RotateFlipType.Rotate90FlipNone)
      originalCopy.Save(file90, System.Drawing.Imaging.ImageFormat.Png)
  
      originalCopy.RotateFlip(RotateFlipType.Rotate90FlipNone)
      originalCopy.Save(file180, System.Drawing.Imaging.ImageFormat.Png)
  
      originalCopy.RotateFlip(RotateFlipType.Rotate90FlipNone)
      originalCopy.Save(file270, System.Drawing.Imaging.ImageFormat.Png)
  
      originalCopy.Dispose()
  
  End Sub
  </code></pre>