As some of you may know, I’m in the process of writing an RSS reader for my Microsoft Smartphone 2003.  It will be running on Net CF.  Since it is an offline RSS reader, I wanted to have the ability to download RSS files for offline reading.  So I had a function, StartDownloadAll() which would iterate through each feed that I’m subscribed to, download the data, eliminate dupes and old items, and save them. In the code, sprinkled throughout, I have lines like:

this.text = “Getting data”;

this.text = “Saving data”;

where this refers to the form.

When I ran through my prototypes, I realized that it was too slow to do this synchronously, I needed to spawn a thread to do this so the UI can still be responsive to the user.  So because C# makes it so easy to create new threads, I ran StartDownloadAll() asynchronously, in its own thread (I’m glossing over some hairy synchronization details, which I’ll get to below).

The problem with doing this is that in C#, you’re not allowed to update a control from a thread that did not create it.  If this was Win32, I would have been more alert, but C# lulled me into a false sense of “everything will work, or else it will throw an exception”.  However…

After I did this, my program started behaving erratically. I’d get random Close() events (and who knows what else could have gone wrong?).  Eventually, I realized my mistake and converted offending functions to use Control.Invoke() (Actually, I used a modified ControlInvoker class).  This leads me to the questions:

  1. Should NetCF throw an exception instead of just providing undefined behavior when someone updates the UI from a separate thread? In light of .Net’s attempts to prevent users from shooting themselves in the foot, would this be a good philosophy to follow? I recall hearing that Avalon will provide this functionality, but it’s just a rumor…  I don’t even work in the Windows division so I probably wouldn’t know :)
  2. Would it be costly to have a class verify that a control was created on a different thread before calling Invoke? I might actually code something up, but I don’t think it will be trivial – I’d probably have to manually keep track of which controls were created on which threads.

OK, enough of that.

I mentioned some hairy synchronization details.  Here’s something that I found out (and probably you .NetCF experts have better workarounds)… suppose I have


void myForm_Closing(object sender, CancelEventArgs e)
{
            lock(this)
            {
                        QuitDownloadingStuff();
            }
}

The QuitDownloadingStuff() method will cause the running download thread to quit, and wait for it to quit before proceeding.  However, as part of the quitting in the other thread, it does the Control.Invoke() equivalent of:

            this.text = “Aborting…”;

This calls back into the UI thread, which is waiting for this function to exit, and thus we end up in a deadlock!  I was sorely confused when this was happening to me – since I didn’t have any double-locking going on.  I fixed this by having QuitDownloadingStuff() return immediately, and I launched a timer to detect whether the thread is actually dead before allowing closing to proceed.

If anyone has any better ways of doing this, let me know, I’m all ears! :)