Dan Byström’s Bwain

Blog without an interesting name

(No) fun with threads

Posted by Dan Byström on August 27, 2004

I came across an “interesting” bug the other day. So interesting in fact that I thought it would make a fun quiz. What’s wrong with this piece of example code?

private void Form1_Load(object sender, System.EventArgs e)
{
  new System.Threading.Thread( new System.Threading.ThreadStart(myThread) ).Start();
}
private void myThread()
{
  foreach ( string strFile in System.IO.Directory.GetFiles( @"c:\pictures","*.jpg" ) )
  {
   if ( pictureBox1.Image!=null )
     pictureBox1.Image.Dispose();
   pictureBox1.Image = new Bitmap( strFile );
   System.Threading.Thread.Sleep( 500 );
  }
}

The answer would be that since the code is executed from a another thread, eventually there will come a day when Windows will decide to repaint the pictureBox1 after we have disposed of the picture in it, but before we have replaced it with a new one! This causes a Windows.Forms exception (which can be easily reproduced by dragging another window in front of the PictureBox while the program is running). Very sneaky I thought.

But then it was pointed out to me that instance methods of the PictureBox class aren’t guaranteed to be thread safe and that the whole piece of code is fundamentally wrong anyway! 😦 *sigh* What an embarassment!

So, to do it correctly, here’s how, just to close the case. The pictureBox1.Image property should only be updated from the thread that owns it. Then there is no chance that we can be interrupted before the new picture is in place! Completely straightforward.

private delegate void SafeUpdatePictureBoxDelegate( Bitmap bmpPicture );
private SafeUpdatePictureBoxDelegate m_delegateSafePictureBoxUpdate;
private void Form1_Load(object sender, System.EventArgs e)
{
  m_delegateSafePictureBoxUpdate = new UppdateraPicBoxDelegate( safePictureBoxUpdate);
  new System.Threading.Thread( new System.Threading.ThreadStart(myThread) ).Start();
}

private void safePictureBoxUpdate( Bitmap bmpPicture )
{
  if ( pictureBox1.Image!=null )
    pictureBox1.Image.Dispose();
  pictureBox1.Image = bmpPicture;
}

private void myThread()
{
  foreach ( string strFile in System.IO.Directory.GetFiles( @"c:\pictures","*.jpg" ) )
  {
    pictureBox1.Invoke( m_delegateSafePictureBoxUpdate,
      new object[] { new Bitmap(strFile) } );
    System.Threading.Thread.Sleep( 500 );
  }
}

Obviously you need to catch exceptions as well as a way to stop the thread. That's it. I wanted to demonstrate how tricky it is to correctly work with threads and was trapped myself. Indeed, that is a good demonstration of how tricky it is!!!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: