Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# Threading Lock error when merging images

I'm trying to make a multithreaded program that takes a certain bitmap from a picturebox where each thread analyzes and changes part of it and then saves it back to the picturebox. I've used a lock() for the instructions that deal with the shared bitmap object and the picturebox but for some reason i still get "Object is currently in use elsewhere" errors every 6-10 runs.

     private Object locker = new Object();

     void doThread(Bitmap bmp2) //simplified - other references not important
     {
        //some code here
        //....  
        lock (locker)
        {
            Graphics gr = Graphics.FromImage(bmp2); //this is where i get the errors, they're related to bmp2
            gr.DrawImage(bmp, new Rectangle(0, 0, 800, 600));
            gr.Dispose();

            pictureBox1.Image = bmp2;
        }
     }

     void runThreads()
     {
        Bitmap bmp2 = new Bitmap(pictureBox1.Image);

        Thread thread1 = new Thread(delegate() { doThread(bmp2); }); 
        Thread thread2 = new Thread(delegate() { doThread(bmp2); });
        Thread thread3 = new Thread(delegate() { doThread(bmp2); });
        Thread thread4 = new Thread(delegate() { doThread(bmp2); });

        thread1.Start();
        thread2.Start();
        thread3.Start();
        thread4.Start();
    }

I've tried to read as much as I could find on the lock() method but it's still a bit unclear so I might have misused it. So my question is, why isn't the lock preventing threads from executing the instructions? Have I misused it? Or is there a workaround I could use?

Any help with this is greatly appreciated.

like image 388
Bogdan Avatar asked Jan 22 '23 16:01

Bogdan


1 Answers

The reason why is that the variable pictureBox1 has an affinity to the GUI thread. You cannot access it and change it's value from a separate background thread. In order to change the value you must do it from the thread the variable is associated with. This is typically done via .Invoke

Try this instead

pictureBox1.Invoke((MethodInvoker)(() => pictureBox1.Image = bmp2));

Even then I think you still have issues because the value bmp2 is used from multiple threads. The variable pictureBox1 will attempt to render this value on the GUI thread while the background thread is creating a graphics object on top of it.

like image 101
JaredPar Avatar answered Jan 30 '23 07:01

JaredPar