2

我正在使用 System.Drawing 类处理图像列表。如果我在正常的 foreach 循环中处理图像,一切都很好,但是当我使用 Parallel.ForEach 遍历图像(图像我不是指任何一次性 System.Drawing.Image 实例,而是图像的源(url))时,我运行记不清。我的进程变得越来越大(> 1GB)。图形缓冲区似乎没有被释放,尽管我很注意释放所有资源。这可以通过切换到非并行 foreach(...) 来确认。该过程保持在大约 60 MB。

你遇到过这样的问题吗?

        // memory "leak". process grows beyound 1GB to infinity
        Parallel.ForEach(urls, url =>
        {
            ImageResizer.DownloadAndResizeImage(url);
        });


        // no memory "leak"
        foreach (string url in urls)
        {
            ImageResizer.DownloadAndResizeImage(url);
        }
4

2 回答 2

3

我希望 Parallel.Invoke 在处理时消耗更多内存,因为它试图一次处理超过 1 个图像。Parallel.Invoke 还将等待所有任务在它返回之前完成,因为你说发生“内存不足”错误,我猜它永远不会返回,所以没有办法知道是否发生内存泄漏。

尝试使用 Parallel.Invoke 只处理两个图像,看看在处理完成后你的内存是否会回到已知的起点。如果是这样,那么就没有内存泄漏-您只是在尝试处理超出系统一次可以处理的内容。

如果是这种情况,请尝试使用 Parallel.ForEach 来限制使用 MaxDegreeOfParallelism 的线程数。

我会尝试的第一件事(假设你有 4 个核心):

Parallel.ForEach( 
    urls, 
    new ParallelOptions { MaxDegreeOfParallelism = 4 }, 
    url => { ImageResizer.DownloadAndResizeImage(url); } 
); 

编辑:

好吧,问题似乎已经从 Parallel.Invoke 更改为 Parallel.ForEach 并添加了一些代码:),但这不应该改变我的答案,因为 Parallel.ForEach 也会等到所有任务都完成。

我猜想方法 DownloadAndResizeImage() 可能是罪魁祸首,而不是并行。为了正确地异步处理请求,使用低级网络 API 需要进行大量开发。使用 Microsoft 的 WebClient 或 HttpWebRequest 对象具有已知的瓶颈,这些瓶颈限制了可以通过多线程异步发出的请求数量。我知道这一点,因为我最近尝试了同样的事情并最终自己编写了套接字层。伊皮!

因此,可能发生的情况是,每个请求都已发出,并且一次只处理两个请求,而其他请求将堆积在队列中等待轮到他们。但是在他们等待的时候,所有的对象都在初始化,导致内存增长。最终(如果您有足够的内存),您将开始看到由于执行时间过长的失败请求而发生了一些超时。

有一些免费软件应用程序旨在筛选数百个请求。我建议获取他们的一些低级代码并在您的应用程序中实现它。这是开始学习套接字的好地方。

http://msdn.microsoft.com/en-us/magazine/cc300760.aspx

于 2012-06-16T12:45:57.557 回答
1

Parallel.ForEach 很容易启动比 CPU 内核更多的线程。看起来内存泄漏因为有 50 个线程或正在运行。

您需要在处理过程中暂停调试器,看看是否有太多线程在运行。

于 2012-06-16T12:55:35.513 回答