Reputation: 1540
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 MessageBox
es 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:
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.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?
Upvotes: 2
Views: 235
Reputation: 66541
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!)
Upvotes: 0
Reputation: 13335
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.
Upvotes: 0