Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/24.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_Winforms - Fatal编程技术网

C# 高效地将许多大型照片加载到面板中

C# 高效地将许多大型照片加载到面板中,c#,.net,winforms,C#,.net,Winforms,如何从一个目录及其子目录加载许多大照片,以防止OutOfMemoryException 我一直在使用: foreach(string file in files) { PictureBox pic = new PictureBox() { Image = Image.FromFile(file) }; this.Controls.Add(pic); } 到目前为止,它一直有效。我现在需要处理的照片的大小在15到40MB之间,可能有数百张。在用户界面上放置时,您可以尝试调整图像大

如何从一个目录及其子目录加载许多大照片,以防止OutOfMemoryException

我一直在使用:

foreach(string file in files)
{
    PictureBox pic = new PictureBox() { Image = Image.FromFile(file) };
    this.Controls.Add(pic);
}

到目前为止,它一直有效。我现在需要处理的照片的大小在15到40MB之间,可能有数百张。

在用户界面上放置时,您可以尝试调整图像大小

foreach(string file in files)
{
    PictureBox pic = new PictureBox() { Image = Image.FromFile(file).resizeImage(50,50) };
    this.Controls.Add(pic);
}

public static Image resizeImage(this Image imgToResize, Size size)
{
   return (Image)(new Bitmap(imgToResize, size));
}

您可以在打开UI时尝试调整图像的大小

foreach(string file in files)
{
    PictureBox pic = new PictureBox() { Image = Image.FromFile(file).resizeImage(50,50) };
    this.Controls.Add(pic);
}

public static Image resizeImage(this Image imgToResize, Size size)
{
   return (Image)(new Bitmap(imgToResize, size));
}

您正在用这种方法攻击垃圾收集器。在循环中加载15-40mb对象将始终引发OutOfMemoryException。这是因为对象直接进入大对象堆,所有大于85K的对象都会这样做。大型对象立即成为Gen 2对象,并且从.Net 4.5.1开始(您请求)内存不会自动压缩,在早期版本中也不会压缩

因此,即使您在最初加载对象时侥幸逃脱,并且应用程序仍在运行,这些对象(即使完全取消引用)也很有可能会挂起,使大型对象堆支离破碎。一旦出现碎片,例如,用户关闭控件以执行其他操作一两分钟,然后再次打开控件,则很可能所有新对象都无法插入LOH-分配时内存必须是连续的。出于性能原因,GC在Gen 2和LOH上运行集合的频率要低得多—GC在后台使用memcpy,这在较大的内存块上非常昂贵

此外,如果从正在使用的控件imagine tabs引用了所有这些图像,则不会释放所消耗的内存。这样做的整个想法是错误的。根据用户需要使用缩略图或加载全尺寸图像,并小心占用内存

更新 我没有告诉你应该做什么和不应该做什么,而是决定试着帮你做:)

我写了一个小程序,在一个包含440个jpeg文件的目录下运行,总大小为335兆字节。当我第一次运行你的代码时,我得到了OutOfMemoryException,表单仍然没有响应

步骤1 首先要注意的是,如果您是以x86或任何CPU进行编译,则需要将其更改为x64。右键单击project,转到Build选项卡并将目标平台设置为x64

这是因为在32位x86平台上可以寻址的内存量是有限的。所有.Net进程都在虚拟地址空间中运行,CLR堆大小将是操作系统允许的任何进程,并且实际上不在开发人员的控制范围内。但是,它将分配尽可能多的可用内存-我在64位Windows 8.1上运行,因此更改目标平台会给我几乎无限的内存空间可供使用-直到您的进程允许的物理内存限制

执行此操作后,运行代码不会导致OutOfMemoryException

步骤2 我在VS 2013中将目标框架从默认的4.5更改为4.5.1。我这样做是为了使用
GCSettings.LargeObjectHeapCompactionMode
,因为它只在4.5.1中可用。我注意到关闭表单需要一段时间,因为GC在释放内存方面做了大量的工作。基本上,我会在loadPics代码的末尾设置它,因为它将允许大型对象堆在下一次阻塞垃圾收集时不会被碎片化。这将是你的应用程序的关键,我相信,如果可能的话,尝试使用这个版本的框架。您也应该在早期版本上测试它,以便在与应用程序交互时看到差异

步骤3 由于应用程序仍然没有响应,我让代码异步运行

步骤4 由于代码现在在UI线程的单独线程上运行,因此在访问表单时会导致GUI跨线程异常,因此我必须使用Invoke,它将消息从代码线程发回UI线程。这是因为只能从UI线程访问UI控件

代码

private async void button1_Click(object sender, EventArgs e)
{
    await LoadAllPics();
}

private async Task LoadAllPics()
{
    IEnumerable<string> files = Directory.EnumerateFiles(@"C:\Dropbox\Photos", "*.JPG", SearchOption.AllDirectories);
    await Task.Run(() =>
    {
        foreach(string file in files)
        {  
            Invoke((MethodInvoker)(() => 
            {
                PictureBox pic = new PictureBox() { Image = Image.FromFile(file) };
                this.Controls.Add(pic);
            }));
        }
    }
    );
    GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
}
private async void按钮1\u单击(对象发送方,事件参数e)
{
等待LoadAllPics();
}
专用异步任务LoadAllPics()
{
IEnumerable files=Directory.EnumerateFiles(@“C:\Dropbox\Photos”、“*.JPG”、SearchOption.AllDirectories);
等待任务。运行(()=>
{
foreach(文件中的字符串文件)
{  
调用((MethodInvoker)(()=>
{
PictureBox pic=new PictureBox(){Image=Image.FromFile(file)};
this.Controls.Add(pic);
}));
}
}
);
GCSettings.LargeObjectHeapCompactionMode=GCLargeObjectHeapCompactionMode.CompactOnce;
}

您正在用这种方法攻击垃圾收集器。在循环中加载15-40mb对象将始终引发OutOfMemoryException。这是因为对象直接进入大对象堆,所有大于85K的对象都会这样做。大型对象立即成为Gen 2对象,并且从.Net 4.5.1开始(您请求)内存不会自动压缩,在早期版本中也不会压缩

因此,即使您在最初加载对象时侥幸逃脱,并且应用程序仍在运行,这些对象(即使完全取消引用)也很有可能会挂起,使大型对象堆支离破碎。一旦出现碎片,例如,用户关闭控件以执行其他操作一两分钟,然后再次打开控件,则很可能所有新对象都无法插入LOH-分配时内存必须是连续的。出于性能原因,GC在Gen 2和LOH上运行集合的频率要低得多—GC在后台使用memcpy,这在较大的内存块上非常昂贵

还有,mem