C # Threading Lock error when merging images

I am trying to make a multi-threaded program that takes a certain bitmap from a window with pictures, where each stream analyzes and modifies part of it, and then saves it back to the image window. I used lock () for instructions that relate to the common bitmap object and file, but for some reason I still get "Object is currently being used somewhere else" errors every 6-10 starts.

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 tried to read as much as the lock () method could find, but it is still a bit unclear, so I might have misused it. So my question is, why does blocking not prevent threads from executing threads? Did I use it incorrectly? Or is there a workaround that I could use?

Any help with this is much appreciated.

+4
source share
3 answers

The error arises because your UI thread uses the image (in particular, setting pictureBox.Image = someImage will cause the .NET ImageAnimator class to look on the image if it is to animate it (for animated. GIF- images, for example).

In the meantime, your background thread changes the image, which causes WinForms code to throw an exception "The object is currently being used elsewhere."

The following code works for me, never crashes, no matter how many threads I throw at it:

 lock (locker) { using (Graphics gr = Graphics.FromImage(bmp2)) { gr.DrawImage(Resources.someImage, new Rectangle(0, 0, 800, 600)); pictureBox1.Invoke(new Action(() => pictureBox1.Image = bmp2)); } } 

Shooting, it turns out, doesn't work either. Throw enough threads and it will work.

I suspect the problem is that Win32 draws your bitmap when the background thread draws on it. Read one (UI) stream, one (background) stream record. This will inevitably lead to problems.

The best solution for multi-threaded errors like this is often to stop exchanging data between threads. Instead, duplicate the data and let each thread have its own local copy. Here is an example:

 lock (locker) { using (Graphics gr = Graphics.FromImage(bmp2)) { gr.DrawImage(Resources.someImage, new Rectangle(0, 0, 800, 600)); var clone = bmp2.Clone() as Image; pictureBox1.Invoke(new Action(() => pictureBox1.Image = clone)); } } 
+3
source

The reason the pictureBox1 variable has an affinity for the GUI thread. You cannot access it and change its value from a separate background thread. To change a value, you must do this from the stream that the variable is associated with. This is usually done using .Invoke

Try this instead

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

Even then, I think you still have problems, because the bmp2 value is used from multiple streams. The variable pictureBox1 will attempt to display this value in the GUI thread, while the background thread creates a graphic object on top of it.

+5
source

Fix the cross-thread issue identified by JaredPar.

Then set pictureBox1.Image to a copy of bmp2.

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

Hope this works for you. If not, you might consider putting together a barebones project that illustrates the problem so that people can run it and futz with code. Threading issus may be too complicated in your head ...

+1
source

Source: https://habr.com/ru/post/1299455/


All Articles