Monday, June 11, 2007

Simplicity is overrated

So I have this new thingy in Sloth v0.1 that downloads some images off the net. It's not very advanced at all, it pretty much just fires off a Google Images search and gets the results (i.e. the first 20 or so pictures), puts them in an owner drawn ListBox, and whenever one is clicked (or when the first one is added), the real size image will be put in a PictureBox.

Nothing to it, right?

Well, I wanted this to be quick without slowing the UI to a crawl, so getting the images from the web would have to happen through the magic of threads. And that is always when things get interesting.

Now, the first thing I did was to start one thread, and have it fetch all 20 images sequentially. Worked fine, but slowly - if the first image was loaded slowly and the next ones quick, well, you didn't get to *see* the next ones before that first slow one was done. So I thought that something as easy and light (cpu wise) as this, why not start one thread per image?

Sure thing, now everything was quick and nice. Images appeared, some quicker than others, but you always got SOME images quickly, at least creating an illusion of speed.

However, as one alpha-tester quickly discovered, there was a problem here - if you closed the window before all the threads had either downloaded their stuff or given up, the program would crash fatally. Not every time, you had to time it just right.

It turned out this happened because the object I was adding the images to (the ListBox, in this case), was disposed. Not only that, it was disposed so that I got bitten by a textbook race condition;

if( control != null && !control.IsDisposed && !control.Disposing )

control.Add( pictureEntity );

Now I would get an exception because the object was disposed in the control.Add() line - a real classical case.

Turns out what I had to do to get it working was to implement a queue, and a timer, and then have the threads insert stuff into the queue instead. Then when the timer fired, the form itself would add any queued items - with the added detail that of course, if the form is closed, the timer is stopped and nothing more happens.

Considering how trivial this really is - is it any wonder why multithreaded programming quickly becomes such a pain?

No comments: