0
votes

Can someone explain why this code:

public static void Main()
{
     int times = 9999;
        Parallel.For(1, times, i =>
        {
            using (Image img = new Bitmap(times, 1600))
            {
                using (Image resized = new Bitmap(img, 10, 10))
                {
                    Console.WriteLine(i);
                }
            }
        });     
}

Gives the following error?

System.AggregateException: One or more errors occurred. ---> System.ArgumentException: Parameter is not valid. Result StackTrace:
at System.Drawing.Bitmap..ctor(Int32 width, Int32 height, PixelFormat format) at System.Drawing.Bitmap..ctor(Int32 width, Int32 height)

I believe the problem is to do with memory usage. I realise I'm making a lot of images all at once, but since each of the images are in a using statement, they should be disposed of correctly?

I realise I've simplified the actual method quite a bit - what this really is, is a unit test. Given an image being created, I'm resizing based on a width, and testing that my code can handle being run in a thread, however it's falling over on the test (above).

UPDATE: This fails:

        int times = 9999;
        Parallel.For(1, times, i =>
        {
            using (Image img = new Bitmap(times, 1600))
            {
                using (Bitmap resized = new Bitmap(img, img.Width, img.Height))
                {
                    //Some code...
                    int width = resized.Width;
                }
            }

        });

But this works (note the missing image parameter in the second bitmap constructor):

        int times = 9999;
        Parallel.For(1, times, i =>
        {
            using (Image img = new Bitmap(times, 1600))
            {
                using (Bitmap resized = new Bitmap(img.Width, img.Height))
                {
                    //Some code...
                    int width = resized.Width;
                }
            }

        });

Any ideas why one works and the other doesn't when they're nearly identical? They both create very large images, both are in using statements to correctly dispose the images, but only the first one is creating an image from an existing image. Why should this make a difference?

3
Does this have to do anything with parallelism? Probably not. So can't you simplify the question to remove parallelism? Just hand-pick one of the inputs that doesn't work. - usr
Note: Bitmap is not ThreadSafe, so the outcome isn't reliable. - danpop

3 Answers

2
votes

You're creating a bitmap that is 9999 x 1600 pixels. Was that intentional, or a typo?

That's a rather large bitmap. You're creating multiple ones at a time (parallelization). If you run it sequentially, it'll probably work.

I was able to make the code work by just changing one line:

using (Image img = new Bitmap(1600, 1600))
0
votes

It may not be disposing fast enough

try this
to test how many are live

public static void Main()
{
        int times = 9999;
        Parallel.For(1, times, i =>
        {
            Console.WriteLine("produce " + i.ToString());
            using (Image img = new Bitmap(times, 1600))
            {
                using (Image resized = new Bitmap(img, 10, 10))
                {
                    Console.WriteLine("resize" + i.ToString());
                }

            }
            Console.WriteLine("dispose" + i.ToString());
        });     
}
0
votes

Well, it still goes back to memory pressure. Use smaller images, don't run parallel, or use you're new second example code.

The original code created a new bitmap scaled from the original. The Framework code to do that probably uses more memory. Creating a 10x10 blank bitmap uses very little memory.