Austin
Austin

Reputation: 299

Windows Forms Parallel Drawing

Is it possible to draw on a panel using a Parallel.For loop? I have a multidimensional array of doubles, double[][] plants, and I want to draw the columns in parallel, where each entry in the array is drawn as a rectangle in a grid on the panel.

On the line with grapahics.FillRectangle(), I keep getting this error when I try:

An exception of type 'System.InvalidOperationException' occurred in System.Drawing.dll but was not handled in user code

Additional information: Object is currently in use elsewhere.

Here is the code I am using:

    Parallel.For(0, simWidth, i =>
        {
            Color plantColor;
            RectangleF plantRectangle= new Rectangle();
            SolidBrush plantBrush = new SolidBrush(Color.Black);
            for (int j = 0; j < simHeight; ++j)
            {
                int r, g = 255, b;
                r = b = (int)(255 * (Math.Tanh(simulation.plants[i, j]) + 1) / 2.0);
                plantColor = Color.FromArgb(100, r, g, b);
                plantBrush.Color = plantColor;
                plantRectangle.Location = new PointF(i * cellSize, j * cellSize);
                graphics.FillRectangle(plantBrush, plantRectangle);
            }

            plantBrush.Dispose();
        });

I think what is happening is that the graphics object cannot handle multiple calls at once. Is there any way around this? I tried creating a local reference to the graphics object in each parallel call but that did not work.

Upvotes: 2

Views: 1621

Answers (1)

Peter Duniho
Peter Duniho

Reputation: 70701

Is it possible to draw on a panel using a Parallel.For loop?

No, not in any way that would actually be useful.

UI objects, such as a Panel, have "thread affinity". That is, they are owned by a particular thread, and must only ever be used in that thread.

GDI+ objects, like your Graphics object (you don't say where you got that object, but one hopes it was passed to you in PaintEventArgs…if not, you have other design flaws in your code) can be more forgiving, but are not thread-safe. You could add synchronization around the actual uses of the object, but those uses are the slow part. Serializing them will negate most of the benefit of concurrency in the code.

Your question does not make clear whether your use of Parallel here was even an actual attempt to address some specific performance problem, never mind what that problem actually was. There are numerous questions with answers on Stack Overflow that discuss various techniques for improving rendering performance in Windows Forms code.

In general, most of these techniques involve reducing the total amount of work done by caching as much as possible. Based on the code you've shown, there are at least two things you might want to do:

  1. Cache the computations for the rectangles and colors. You can even do that part of the computation with Parallel, whenever the underlying parameters change.
  2. Draw everything into a Bitmap object. This will have to be done single-threaded, but a) it doesn't have to be done in the UI thread that owns your UI objects, and b) you (again) can do this just once, whenever the underlying parameters change. Having drawn into a Bitmap, then you can just draw the Bitmap object when the Paint event occurs, instead of having to re-render everything from scratch.

Upvotes: 2

Related Questions