【发布时间】:2017-08-23 18:01:11
【问题描述】:
我正在将大量图像并行调整为 1000x1000 缩略图,并且很快就会耗尽内存。 (性能分析器在大约 3 分钟后将我的已用内存设为 3GB)
最初我使用Image.FromFile(),但做了一些研究,我发现Image.FromStream() 是要走的路。我认为我有适当的 using 语句,某处的东西仍在内存中,而 GC 没有按预期清除资源。
似乎 GDI+ 保持手柄打开存在问题,但我似乎找不到适合我的情况的解决方案。
问题:
- 我做错了什么吗?
- 如果没有,有没有更好的方法来
Dispose()的流/图像/ResizedImage,这样我就不会耗尽所有资源,同时仍然保持快速的并行操作? - 如果 GDI+ 是问题所在并且使非托管资源保持活动状态,我该如何解决该问题?
代码
List<FileInfo> files 包含 ~300 张有效的 JPG 图片,每个 JPG ~2-4mb
来电者
public void Execute()
{
Parallel.ForEach(Files, (file) =>
{
Resize.ResizeImage(file.FullName);
}
);
}
Execute() 调用 Parallel.Foreach()..
调整类的大小
public static class Resize
{
public static void ResizeImage(string fileName)
{
ResizeImage(fileName, 1000, 1000, true);
}
public static void ResizeImage(string fileName, int newHeight, int newWidth, bool keepAspectRatio = true)
{
string saveto = Path.GetDirectoryName(fileName) + @"\Alternate\" + Path.GetFileName(fileName);
try
{
using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
using (Image ImageFromStream = Image.FromStream(fs))
{
var ImageSize = new Size(newHeight, newWidth);
if (keepAspectRatio)
{
int oWidth = ImageFromStream.Width;
int oHeight = ImageFromStream.Height;
double pWidth = ((double)ImageSize.Width / (double)oWidth);
double pHeight = ((double)ImageSize.Height / (double)oWidth);
double percent;
if (pHeight < pWidth)
percent = pHeight;
else
percent = pWidth;
newWidth = (int)(oWidth * percent);
newHeight = (int)(oHeight * percent);
}
else
{
newWidth = ImageSize.Width;
newHeight = ImageSize.Height;
}
var ResizedImage = new Bitmap(newWidth, newHeight);
using (Graphics gfxHandle = Graphics.FromImage(ResizedImage))
{
gfxHandle.InterpolationMode = InterpolationMode.HighQualityBicubic;
gfxHandle.DrawImage(ImageFromStream, 0, 0, newWidth, newHeight);
if (!Directory.Exists(Path.GetDirectoryName(saveto))) { Directory.CreateDirectory(Path.GetDirectoryName(saveto)); }
ResizedImage.Save(saveto, ImageFormat.Jpeg);
}
ResizedImage.Dispose();
ResizedImage = null;
}
}
}
catch (Exception ex)
{
Debug.WriteLine(string.Format("Exception: {0}", ex.Message));
}
}
【问题讨论】:
-
我确实记得听说过 GDI+ 没有正确释放资源,但那是不久前的事了,所以我对细节很粗略。您可以做的是使用
GC.Collect()强制垃圾收集循环,或者使用显式工作线程限制并行调整大小的数量。或者甚至两者兼而有之。 -
@ZacFaragher A
GC.Collect()在这种情况下不会做任何事情。我在看性能分析器,垃圾人来收拾东西,但它不在路边。不过,您对并行调整大小的数量是正确的,您刚刚向我指出了解决方案,谢谢。
标签: c# .net out-of-memory gdi+ parallel.foreach