2

I'm writing a small .NET (Windows Forms) application, but I can't seem to get the multithreading right.

The application has a very simple logic: it displays a main form with some parameters to fill in, a button to start processing and a progress bar; processing starts when the user clicks on the button, the progress bar should be updated while the program runs, some MessageBoxes can pop up while processing in order to display warnings/informations, and when processing ends another MessageBox pops up with the results. Fairly simple... but I can't seem to find a way to handle control updates correctly.

First I tried the simple route: doing all of my processing in the Button_Clicked event. This didn't go well: it worked, but the form became completely unresponsive while processing, it didn't care about any user click at all. I assumed this was because it was actually busy handling an event, thus stopping processing other ones... so I went with multithreading.

In the button handling event I started a new Thread and made it do the processing, and then called Thread.Join on it; MSDN documentatin says about Thread.Join: "Blocks the calling thread until a thread terminates, while continuing to perform standard COM and SendMessage pumping" (http://msdn.microsoft.com/en-us/library/95hbf2ta.aspx). But this doesn't seem to be the case: after calling Thread.Join, the form became unresponsive again, until the thread terminated. Why is this? I ditched the Join idea and immediately returned from the event handling function (disabling buttons on the form to avoid users starting more than one processing at the same time), but then I met another problem: I started getting exceptions when trying to update the progress bar, the runtime complained about having called a control from a thread that was not its owner.

So I finally went with a BackgroundWorker: I added one to the form, called its RunWorkerAsync() method and used ReportProgress() to fire a ProgressChanged event and handle it in the main form (to avoid the cross-thread control update problem). I was finally able to update the progress bar... but now this is completely asynchronous: the progress bar is actually updated at some random interval after the worker thread calls ReportProgress() (even one second!), and this gives a particularly ugly visual effect. Two examples:

  • If I call ReportProgress() from the worker thread and then MessageBox.Show() to display some message about the current processing step, I can see the progress bar advancing after the MessageBox pops up.
  • I have a RunWorkerCompleted event which pops up the last MessageBox with the results, and I can see it popping up on screen while the progress bar is still filling its last steps.

What am I missing here?

Massimo
  • 1,520
  • 1
  • 19
  • 36
  • It seems, you have missed nothing. Is the messagebox is necessary? If you remove it, does that also decrease the ugliness? – Kangkan Feb 09 '12 at 08:21
  • Yes, the application logic requires them, both while processing and at the end. Of course I could put some `Thread.Sleep()` in there, but this looks like a really ugly hack... – Massimo Feb 09 '12 at 08:34

3 Answers3

0

By far the best way, IMHO, to handle asynchrony in WinForm (and WPF) apps is Reactive Extensions. Bit of a curve to go through but once you get it you won't go back.

Myles McDonnell
  • 12,943
  • 17
  • 66
  • 116
0

add Application.DoEvents() to update the UI where it is needed.

Jaster
  • 8,255
  • 3
  • 34
  • 60
0

Well you've tried quite a few things. Here are a few tips, maybe not the right answer for you, but could help you.

To overcome the "updating control from other thread" problem, you can use the Invoke or BeingInvoke commands on the control (I use an extension method for this).

That aside, I think that Kangkan's comment is legitimate - using a MessageBox is just adding pain to your problem, because it is modal and it blocks the UI from updating. Are you SURE that the MessageBox is appropriate - do you absolutely need user input before continuing? Or do you just need to inform them? If the latter, you should consider using something more like a status bar, or the system messages you get here on StackOverflow (imagine if they were all alerts!)

Community
  • 1
  • 1
Benjol
  • 63,995
  • 54
  • 186
  • 268