ConcurrentQueue Operation给出OutofMemory错误。

时间:2014-04-07 15:26:29

标签: c# multithreading concurrent-queue

我理解这个问题可能过于笼统。但我尝试了很多东西,我无法弄清楚如何解决这个问题。

我使用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);

         }

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

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时,我都会收到MemoryFull错误。我不知道如何避免这种情况。我尝试过使用Thread.Sleep来避免这种情况。例如,我在Thread.Sleep(500)循环后尝试foreach。每当我在foreach内尝试它时,它在任何给定点ImageQueue.Count = 1都可以正常工作。哪里出错了?

5 个答案:

答案 0 :(得分:1)

您实际上从未从队列中删除图像。你遍历它们,但你永远不会将它们出列。您还有一个问题,即在枚举队列时,只要当前队列中没有其他项目,就会停止枚举。你不想这样做,你可能还没有完成。

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

除了进行此更改之外,如Guffa的答案所述,您需要处理图像对象。完成图像后,您可以将它们丢弃在循环体的末端。

答案 1 :(得分:0)

您正在创建许多您不会处置的Image个对象。您应该在您创建的每个缩略图上调用Dispose

此外,您出列的对象包含一个Image对象,当您不再需要它时,您应该将其丢弃。


附注:你不应该枚举队列中的项目并且在循环中定义项目。而是在循环中使用TryDequeue方法:

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

另一个注意事项:当队列为空时,您的代码将进入紧密循环,使用大量CPU功率重复进行相同的检查。您应该使用一种机制来暂停线程,直到队列发生某种情况,或者至少再次睡眠一段时间再重新检查队列。

答案 2 :(得分:0)

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

答案 3 :(得分:0)

1.似乎你正在保留对所有图像的引用,这些图像将它们保存在内存中,不允许垃圾收集器收集它们。

此外,在您使用完对象后,您可以明确地处理它们,如下所示:

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

2。你正在做的是每次你在while循环中枚举你的所有图像,我不确定这是你想要的。你应该做的是尝试将项目出列:

FileImageName myImage;
ImageQueue.TryDequeue(out myImage);

if (myImage != null)
{
// Do work on the image
}

答案 4 :(得分:0)

您可能希望查看BlockingCollection类(here),我已成功将其用于此类“生产者 - 消费者”模式。

某物(生产者)将物品放入集合中,一个或多个“消费者”b / g线程从集合中取出下一个可用物品并用它们做某事。您还可以配置要使用的使用者线程数(例如,对于多核PC)。