Access to object being locked - exception: The object is currently in use elsewhere.

6

6tc1

Hi all, I have just finished debugging a windows application and have
solved the problem - however, I want to be sure that I understand the
problem before I move on. Before I detail the problem, this problem
requires some understanding of threading concepts.

Basically, the class contained both a PictureBox object as well as a
corresponding Image object (they both had a copy of the same picture).


The following was used to access the the Image object (I believe the
setter below will result in them both getting a copy of the same image
- but they won't both be pointing at the same image):
[CategoryAttribute("Appearance")]
[DescriptionAttribute("Image1")]
[DefaultValueAttribute(null)]
public Image Image1
{
get { return _image1; }
set
{
_image1 = value;
pictureBox1.Image = value;
}
}

_image1 is an object of type Image and pictureBox1 is an object of type
PictureBox.

Now as you have probably deduced, there is no obviously good reason to
have both an Image object contained in this class, since the two are
always in sync. Now, no problems emerged from this until we made this
application multi-threaded.

Once we made it multi-threaded I got the following error:
The object is currently in use elsewhere.
at System.Drawing.Image.get_Width()
at CustomPictureBox.ScaleImages(Single scaleFactor, Boolean
arrangeHorizontally) in c:\test\custompicturebox.cs:line 433
at CustomPictureBox.OnResize(Object sender, EventArgs e) in
c:\test\autopicturebox.cs:line 282

By simply removing the redundant Image object from the class and of
course any use of that member variable the problem went away.

Now in an attempt to get the most from the experience I have attempted
to make a small application to reproduce the problem to show other
people why this happens - but I can't get a small application to
reproduce the problem even though I understand the essence of the
problem.

I would be happy to send this small program to others to see if that
can "fix it" so that it will break in the above manner. But
essentially I'm just looking a bare bones windows application that
loads a picture, uses multi-threading and then results in a race
condition because of code similar to the above.

Thanks,
Novice
 
6

6tc1

Sorry, I meant to say that they _are_ pointing at the same Image object
- this is why there is a race condition. They do not have a copy of
the same image.
 
N

Nicholas Paldino [.NET/C# MVP]

What exactly are you trying to do when the exception gets thrown? That
code would help tremendously.

The one thing that I can definitely tell you is that if you are going to
perform any operations on the PictureBox (and it seems you are doing
something, either directly or indirectly from the text of the exception),
then you should do it in the UI thread. Windows controls need to have calls
to them (for the most part) made on the thread they were created on. If you
don't, then you get unexplained results.

If you are doing this, then you might want to reconsider the
multithreaded approach, or make your methods on this class thread safe.

I would post the example that you tried to draw up as well.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Hi all, I have just finished debugging a windows application and have
solved the problem - however, I want to be sure that I understand the
problem before I move on. Before I detail the problem, this problem
requires some understanding of threading concepts.

Basically, the class contained both a PictureBox object as well as a
corresponding Image object (they both had a copy of the same picture).


The following was used to access the the Image object (I believe the
setter below will result in them both getting a copy of the same image
- but they won't both be pointing at the same image):
[CategoryAttribute("Appearance")]
[DescriptionAttribute("Image1")]
[DefaultValueAttribute(null)]
public Image Image1
{
get { return _image1; }
set
{
_image1 = value;
pictureBox1.Image = value;
}
}

_image1 is an object of type Image and pictureBox1 is an object of type
PictureBox.

Now as you have probably deduced, there is no obviously good reason to
have both an Image object contained in this class, since the two are
always in sync. Now, no problems emerged from this until we made this
application multi-threaded.

Once we made it multi-threaded I got the following error:
The object is currently in use elsewhere.
at System.Drawing.Image.get_Width()
at CustomPictureBox.ScaleImages(Single scaleFactor, Boolean
arrangeHorizontally) in c:\test\custompicturebox.cs:line 433
at CustomPictureBox.OnResize(Object sender, EventArgs e) in
c:\test\autopicturebox.cs:line 282

By simply removing the redundant Image object from the class and of
course any use of that member variable the problem went away.

Now in an attempt to get the most from the experience I have attempted
to make a small application to reproduce the problem to show other
people why this happens - but I can't get a small application to
reproduce the problem even though I understand the essence of the
problem.

I would be happy to send this small program to others to see if that
can "fix it" so that it will break in the above manner. But
essentially I'm just looking a bare bones windows application that
loads a picture, uses multi-threading and then results in a race
condition because of code similar to the above.

Thanks,
Novice
 
6

6tc1

Yeah, I wanted to post the code, but I figured it would be too much to
digest in one post. Here is what I have tried - you will of course
have to change the file (test.bmp) that is getting loaded or create a
file of that name in your working directory (usually the directory your
binary gets compiled into). This application works - I would like to
tweak it so that I get the aforementioned exception when I hit the
button - I tried doing all sorts of processing in Foo before I call
Foo2, but that didn't seem to help:

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

using System.Threading;
using System.IO;

using System.Drawing.Imaging;

namespace ThreadTest
{
/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.Button button1;

//private Another another;
private System.Windows.Forms.PictureBox pictureBox1;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;

public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
}

/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}

#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.textBox1 = new System.Windows.Forms.TextBox();
this.button1 = new System.Windows.Forms.Button();
this.pictureBox1 = new System.Windows.Forms.PictureBox();
this.SuspendLayout();
//
// textBox1
//
this.textBox1.Dock = System.Windows.Forms.DockStyle.Bottom;
this.textBox1.Location = new System.Drawing.Point(0, 390);
this.textBox1.Multiline = true;
this.textBox1.Name = "textBox1";
this.textBox1.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.textBox1.Size = new System.Drawing.Size(576, 192);
this.textBox1.TabIndex = 3;
this.textBox1.Text = "";
//
// button1
//
this.button1.Dock = System.Windows.Forms.DockStyle.Bottom;
this.button1.Location = new System.Drawing.Point(0, 367);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(576, 23);
this.button1.TabIndex = 1;
this.button1.Text = "Start";
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// pictureBox1
//
this.pictureBox1.Dock = System.Windows.Forms.DockStyle.Fill;
this.pictureBox1.Location = new System.Drawing.Point(0, 0);
this.pictureBox1.Name = "pictureBox1";
this.pictureBox1.Size = new System.Drawing.Size(576, 367);
this.pictureBox1.TabIndex = 2;
this.pictureBox1.TabStop = false;
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(576, 582);
this.Controls.Add(this.pictureBox1);
this.Controls.Add(this.button1);
this.Controls.Add(this.textBox1);
this.Name = "Form1";
this.Text = "Thread Test";
this.ResumeLayout(false);

}
#endregion

Image _image1;

[CategoryAttribute("Appearance")]
[DescriptionAttribute("Image1")]
[DefaultValueAttribute(null)]
public Image Image1
{
get { return _image1; }
set
{
_image1 = value; pictureBox1.Image = value;
}
}

private void LoadImage (){
String image_filename = "test.bmp";
if(File.Exists(image_filename))
{
try
{
Image1 = Image.FromFile( image_filename );
}
catch
{
Image1 = null;
}
}
}

private void Foo ()
{
try
{
int imageWidth = (Image1 != null) ? Image1.Width : 0;
pictureBox1.Width = imageWidth + 100;
int imageHeight = Image1.Height;
double widthAsDouble = imageWidth;
double testing = 0f;
for (int i = 0;i < 20; i++){
testing += Image1.Width * Math.Max(Math.PI, widthAsDouble);
}

Foo2();
}
catch (Exception exception)
{
String exceptionStr = "exception:
"+exception.Message+System.Environment.NewLine + exception.StackTrace +
System.Environment.NewLine + System.Environment.NewLine;
textBox1.AppendText(exceptionStr);
}
}

private void Foo2 ()
{
try
{
int imageWidth = (Image1 != null) ? Image1.Width : 0;
pictureBox1.Width = imageWidth + 100;
}
catch (Exception exception)
{
String exceptionStr = "exception:
"+exception.Message+System.Environment.NewLine + exception.StackTrace +
System.Environment.NewLine + System.Environment.NewLine;
textBox1.AppendText(exceptionStr);
}
}

private void ThreadedMethod(object JustTesting)
{
LoadImage();
Foo();
}

private void button1_Click(object sender, System.EventArgs e)
{
try
{
Toub.Threading.ManagedThreadPool.QueueUserWorkItem( new
WaitCallback(ThreadedMethod), null );
}
catch (Exception exception)
{
String exceptionStr = "exception:
"+exception.Message+System.Environment.NewLine + exception.StackTrace +
System.Environment.NewLine + System.Environment.NewLine;
textBox1.AppendText(exceptionStr);
}
}

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new Form1());
}

}
}
 
6

6tc1

I got it to happen... but I created a separate UserControl, threw the
Foo and Foo2 methods in there and bingo... not really happy with that,
because in the application it was like 90% reproducible... in this test
application it only happens 5 or 10% of the time. I realize I could
just create a tight loop and generate a bunch of threads and have them
access it - but I want it to occur without having multiple threads in
the Foo call stack - I want this error to occur because of these extra
references to the Image object.

Anyway, I'm guessing this is the end of this thread...

Novice
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top