并发队列操作出现内存不足错误

本文关键字:内存不足 错误 队列 操作 并发 | 更新日期: 2023-09-27 17:57:14

我知道这个问题可能太笼统了。但是我尝试了很多事情,但我无法弄清楚如何解决这个问题。

我正在使用ConcurrentQueue进行多线程操作。一个线程正在从服务器下载图像并将其保存到队列中。这是代码:

 public static void DownloadImage()
    {
        string baseUrl = "http://someurl";
        //int numIterations = 5;
        HttpWebRequest request = null;
        foreach (var fileName in fileNames)
        {
                string url = string.Format(baseUrl, fileName);
                request = (HttpWebRequest)WebRequest.Create(url);
                request.Method = "GET";
                request.ContentType = "application/x-www-form-urlencoded";
                var response = (HttpWebResponse)request.GetResponse();
                Stream stream = response.GetResponseStream();
                img = Image.FromStream(stream);
                ImageFileName FileNameImage = new ImageFileName(fileName, img);
                ImageQueue.Enqueue(FileNameImage);
                Console.WriteLine("Count after Enqueue: {0}", ImageQueue.Count);
         }

另一个线程从队列中获取图像并将它们保存在目标文件夹中。这是代码:

public static void SaveImage()
    {
        while (true)
        {
            if (!ImageQueue.IsEmpty)
            {
                foreach (var newobject2 in ImageQueue)
                {
                    Image img2 = newobject2.Image;
                    img2.Save("C:''path" + newobject2.ImageName);
                    ZoomThumbnail = img2;
                    ZoomSmall = img2;
                    ZoomLarge = img2;
                    ZoomThumbnail = GenerateThumbnail(ZoomThumbnail, 86, false);
                    ZoomSmall = GenerateThumbnail(ZoomSmall, 400, false);
                    ZoomLarge = GenerateThumbnail(ZoomLarge, 1200, false);
                    ZoomThumbnail.Save("C:''path" + newobject2.ImageName + "_Thumb.jpg");
                    ZoomSmall.Save("C:''path" + newobject2.ImageName + "_ZoomSmall.jpg");
                    ZoomLarge.Save("C:''path" + newobject2.ImageName + "_ZoomLarge.jpg");
                    ImageFileName imgobject3 = new ImageFileName();
                    ImageQueue.TryDequeue(out imgobject3);
                    Console.WriteLine("Count after Deque: {0}", ImageQueue.Count);
                }

            }

        }
    }

我像这样调用来自 Button_Click() 的两个线程:

Thread DownloadThread = new Thread(DownloadImage);
  DownloadThread.Start();
  Thread SaveThread = new Thread(SaveImage);
  SaveThread.Start();

每当队列达到 68 计数时,我都会收到内存已满错误。我不确定如何避免这种情况。我尝试使用Thread.Sleep来避免这种情况。例如,我尝试:Thread.Sleep(500)循环后foreach。每当我在foreach里面尝试它时,它都可以像ImageQueue.Count = 1的任何给定点一样工作正常。我哪里错了?

并发队列操作出现内存不足错误

您从未真正从队列中删除图像。 您可以遍历它们,但从不将它们取消排队。 您还会遇到一个问题,即在枚举队列时,只要队列中当前没有更多项目,枚举项就会停止。 你不想这样做,你可能还没有完成。

您要做的是使用 BlockingCollection 而不是 ConcurrentQueue ,这样如果当前没有更多项目,您可以等待更多项目,而不是停止。 完成此操作后,您可以使用 foreach(var item in queue.GetConsumingEnumerable()) . 这将进行所有需要的更改。 它会在您迭代队列时从队列中删除项目,如果当前没有项目,它会等待更多项目。

除了进行此更改之外,如 Guffa 的回答中所述,您还需要释放图像对象。 处理完图像后,您可以在循环体的末端处理它们。

您应该在完成图像处理后进行处理,以便在 ZoomLarge 语句之后进行处理,因为在那之后您不再使用它。使用 Generate 语句可以创建新映像。

你可能

想看看BlockingCollection类(这里),我已经成功地将其用于这种"生产者-消费者"模式。

某些东西(生产者)将项目放入集合中,一个或多个"消费者"b/g 线程从集合中取出下一个可用项目并对其进行处理。您还可以配置要使用的使用者线程数(例如,对于多核 PC)。

您正在创建许多不释放的Image对象。您应该对创建的每个缩略图调用Dispose

此外,取消排队的对象包含一个Image对象,当您不再需要它时,应释放该对象。


旁注:不应枚举队列中的项,也不应取消循环中的项。而是在循环本身中使用 TryDequeue 方法:

ImageFileName imgobject2;
while (ImageQueue.TryDequeue(out imgobject2)) {
  ...
}

另一个旁注:当队列为空时,您的代码将进入紧密循环,使用大量 CPU 功率非常快速地重复进行相同的检查。您应该使用一种机制来挂起线程,直到队列出现问题,或者至少休眠一段时间,然后再再次检查队列。

1.It 似乎

您保留了对所有图像的引用,这些图像将它们保存在内存中,不允许垃圾回收器收集它们。

此外,在使用

完对象后,可以显式释放它们,如下所示:

Image img2 = newobject2.Image;
img2.Dispose();

2.您正在做的是每次进入一段时间循环时枚举所有图像,我不确定这是您想要的。您应该做的是尝试将项目取消排队:

FileImageName myImage;
ImageQueue.TryDequeue(out myImage);
if (myImage != null)
{
// Do work on the image
}