Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 并行调整图像大小导致内存不足异常_C#_.net_Out Of Memory_Gdi+_Parallel.foreach - Fatal编程技术网

C# 并行调整图像大小导致内存不足异常

C# 并行调整图像大小导致内存不足异常,c#,.net,out-of-memory,gdi+,parallel.foreach,C#,.net,Out Of Memory,Gdi+,Parallel.foreach,我正在将大量图像并行调整为1000x1000个缩略图,并且很快就会耗尽内存。(Performance profiler让我在大约3分钟后拥有3GB的已用内存) 最初我使用的是Image.FromFile(),但是做了一些研究,我发现Image.fromfstream()是一个不错的选择。我认为我有适当的using语句,某些东西仍然保存在内存中,GC没有按预期清除资源 GDI+保持句柄打开似乎有问题,但我似乎找不到适合我的案例的解决方案 问题: 我做错什么了吗 如果没有,有没有更好的方法来处理流/

我正在将大量图像并行调整为1000x1000个缩略图,并且很快就会耗尽内存。(Performance profiler让我在大约3分钟后拥有3GB的已用内存)

最初我使用的是
Image.FromFile()
,但是做了一些研究,我发现
Image.fromfstream()
是一个不错的选择。我认为我有适当的using语句,某些东西仍然保存在内存中,GC没有按预期清除资源

GDI+保持句柄打开似乎有问题,但我似乎找不到适合我的案例的解决方案

问题:

  • 我做错什么了吗
  • 如果没有,有没有更好的方法来处理流/image/ResizedImage的
    Dispose()
    ,这样我就不会耗尽所有的资源,同时还能保持快速的并行操作
  • 如果GDI+是问题所在,并且使非托管资源保持活动状态,那么如何纠正该问题
  • 代码
    列表文件
    包含约300个有效JPG图像,每个JPG图像约2-4mb

    呼叫者
    Execute()
    调用Parallel.Foreach()

    调整类大小
    公共静态类调整大小
    {
    公共静态void ResizeImage(字符串文件名)
    {
    调整图像大小(文件名,1000,1000,true);
    }
    公共静态void ResizeImage(字符串文件名,int-newHeight,int-newWidth,bool-keepasspectratio=true)
    {
    字符串saveto=Path.GetDirectoryName(文件名)+@“\Alternate\”+Path.GetFileName(文件名);
    尝试
    {
    使用(FileStream fs=newfilestream(文件名,FileMode.Open,FileAccess.Read))
    {
    使用(Image-ImageFromStream=Image.FromStream(fs))
    {
    var ImageSize=新尺寸(新高度、新宽度);
    if(keepAspectRatio)
    {
    int oWidth=ImageFromStream.Width;
    int oHeight=ImageFromStream.Height;
    双pWidth=((双)图像大小.Width/(双)宽度);
    双光=((双)图像大小.高度/(双)宽度);
    百分之二;
    if(高度<宽度)
    百分比=pHeight;
    其他的
    百分比=宽度;
    新宽度=(整数)(宽度*百分比);
    新高度=(整数)(oHeight*百分比);
    }
    其他的
    {
    newWidth=ImageSize.Width;
    newHeight=ImageSize.Height;
    }
    var ResizedImage=新位图(新宽度、新高度);
    使用(Graphics gfxHandle=Graphics.FromImage(ResizedImage))
    {
    gfxHandle.InterpolationMode=InterpolationMode.HighQualityBicubic;
    gfxHandle.DrawImage(ImageFromStream,0,0,newWidth,newHeight);
    如果(!Directory.Exists(Path.GetDirectoryName(saveto)){Directory.CreateDirectory(Path.GetDirectoryName(saveto));}
    ResizedImage.Save(保存到,ImageFormat.Jpeg);
    }
    ResizedImage.Dispose();
    ResizedImage=null;
    }
    }
    }
    捕获(例外情况除外)
    {
    WriteLine(string.Format(“异常:{0}”,例如Message));
    }
    }
    并行性的
    指出我的
    Parallel.ForEach()
    基本上是在创建过多的新任务,因为它正在等待磁盘访问。在大约5分钟的时候,大约在引发异常时,大约有160个线程。降低并行度会限制创建的线程数量,以及在内存中等待完成加载或写入dis的映像数量设置
    MaxDegreeOfParallelism=2
    似乎是网络磁盘访问的最佳选择,它将线程数减少到25个左右,并将CPU利用率提高到35%左右(从17%提高到24%,这是由于GC阻塞线程,以及太多线程造成的CPU开销)


    谢谢@ZacFaragher。

    我确实记得听说GDI+没有正确释放资源,但那是很久以前的事了,所以我对细节略知一二。你可以做的是使用
    GC.Collect()强制执行垃圾收集循环
    ,或者使用显式工作线程限制并行大小调整的数量。或者两者都可以。@ZacFaragher A
    GC.Collect()
    在这种情况下不会起任何作用。我正在观看性能分析器,垃圾工过来捡东西,但它不在路边。不过,关于并行大小调整的数量,您是对的,您刚刚为我指出了解决方案,谢谢。做得好,做得好!今天我了解到
    parallel.ForEach()
    存在!
        public void Execute()
        {
            Parallel.ForEach(Files, (file) =>
            {
                Resize.ResizeImage(file.FullName);
            }
            );
        }
    
    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));
            }
        }
    
        public void Execute()
        {
            //Parallel.ForEach(Files, (file) =>
            //{
            //    Resize.ResizeImage(file.FullName);
            //}
            //);
    
            Parallel.ForEach(Files, new ParallelOptions() { MaxDegreeOfParallelism = 2 }, (file) => { Resize.ResizeImage(file.FullName); } );
    
        }