开发者

InvalidOperationException - object is currently in use elsewhere

I've gone through this SO question but it didn't help.

The case here is different. I'm using Backgroundworkers. 1st backgroundworker starts operating on the image input of user and inside firstbackgroundworker_runworkercompleted() I'm using calling 3 other backgroundworkers

 algo1backgroundworker.RunWorkerAsync();
 algo2backgroundworker.RunWorkerAsync();
 algo3backgroundworker.RunWorkerAsync();

this is the code for each:

algo1backgroundworker_DoWork()
{
 Image开发者_StackOverflow img = this.picturebox.Image;
 imgclone = img.clone();
 //operate on imgclone and output it
}

algo2backgroundworker_DoWork()
{
 Image img = this.picturebox.Image;
 imgclone = img.clone();
 //operate on imgclone and output it
}

similar operations are done in other algo*backgrougrondworker_doWork().

Now SOMETIMES I'm getting "InvalidOperationException - object is currently in use elsewhere". Its very arbitrary. I somtimes get this in algo1backgroundworker_DoWork and sometimes in algo2backgroundworker_DoWork and sometimes in Application.Run(new myWindowsForm());

I've no clue about whats happening.


There's a lock inside GDI+ that prevents two threads from accessing a bitmap at the same time. This is not a blocking kind of lock, it is a "programmer did something wrong, I'll throw an exception" kind of lock. Your threads are bombing because you are cloning the image (== accessing a bitmap) in all threads. Your UI thread is bombing because it is trying to draw the bitmap (== accessing a bitmap) at the same time a thread is cloning it.

You'll need to restrict access to the bitmap to only one thread. Clone the images in the UI thread before you start the BGWs, each BGW needs its own copy of the image. Update the PB's Image property in the RunWorkerCompleted event. You'll lose some concurrency this way but that's unavoidable.


So it looks like your BackgroundWorkers are trying to access the same Windows Forms components at the same time. This would explain why the failure is random.

You'll need to make sure this doesn't happen by using a lock, perhaps like so:

private object lockObject = new object();

algo1backgroundworker_DoWork()
{
    Image imgclone;
    lock (lockObject)
    {
        Image img = this.picturebox.Image;
        imgclone = img.clone();
    }

    //operate on imgclone and output it
}

Note that I make sure that imgclone is local to this method - you definitely don't want to share it across all the methods!

On the other hand the same lockObject instance is used by all the methods. When a BackgroundWorker method enters its lock{} section, others that come to that point will be blocked. So it's important to make sure that the code in the locked section is fast.

When you come to "output" your processed image, be careful too to make sure that you don't do a cross-thread update to the UI. Check this post for a neat way to avoid that.


In windows forms not only should you only access the controls from a single thread but that thread should be the main application thread, the thread that created the control.

This means that in DoWork you should not access any controls (without using Control.Invoke). So here you would call RunWorkerAsync passing in your image clone. Inside the DoWork event handler, you can extract the parameter from the DoWorkEventArgs.Argument.

Only the ProgressChanged and RunWorkerCompleted event handlers should interact with the GUI.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜