Reputation: 397
I have an application that needs to wait for a background worker to finish before moving on. The background worker is in charge of writing some values to a USB device that initializes it. The problem with the background worker is that my main program can try to access the USB device before the background worker is finished initializing.
While the background worker is working, the UI thread shows a "please wait" with an animated progress bar. The progress bar does not reflect how far along the background worker is, it merely 'spins'.
I have read a few questions that say not to use a background worker, because I really don't want it to run asynchronously (which is true), however, without using a background worker, my "please wait" dialog blocks and doesn't show the animation. I have also read a lot that tells me to only use one UI thread, which then supports the use of a background worker.
I have also tried to put the "please wait" spinner in a separate thread, but that introduces complexities and strange race conditions where the "please wait" windows tries to close before it has opened.
What is the right way to go about this?
Upvotes: 1
Views: 3281
Reputation: 27633
Put the part that has to be executed after the BackgroundWorker
has finished (-the script) in the BackgroundWorker
's RunWorkerCompleted
event handler.
Upvotes: 2
Reputation:
I think there are two separate issues you are trying to resolve.
The first one is how to prevent the Main (UI) thread accessing an uninitialized USB device. The other one is handling the "Please Wait" spinner dialog.
My suggestions are:
BackgroundWworker
handling the USB device initialization.Add a boolean field to your class, to flag when the USB device completed initialization e.g.
private volatile bool _usbDeviceInited = false;
The last thing your background worker will do, is set this flag to true
.
The previous steps are one way to solve your first issue. Now, for the spinner.
I suggest suscribing to the RunWorkerCompleted
event of your BackgroundWorker
. If your BackgroundWorker
was created from the main UI thread, the event will fire in the main UIThread (meaning it's safe to interact with the user interface elements). From there you can simply close and dispose your "Please Wait" dialog. You should also check the AsyncCompletedEventArgs.Error
property to ensure no error condition was raised from the BackgroundWorker thread while initializing the USB device.
EDIT: I have just read that your app may run a script that immediately tries to access the USB device. Using the pattern I suggested, you may "poll" the property before running the script until it returns a value different than null like this:
bool timedOut = false;
DateTime timeout = DateTime.Now.AddSeconds(timeoutSeconds);
while ( MyClass.MyUsbDevice == null ) {
if( DateTime.Now > timeout ) {
timedOut = true;
break;
}
Thread.Sleep(0); // Avoid pegging the CPU, yield it to other processes
}
if( !timedOut ) {
// run the script
} else {
// Handle timeout
}
Good Luck!
Upvotes: 2
Reputation: 82096
I have read a few questions that say not to use a background worker
Don't really know why people have suggested not to use this as it's exactly what it was designed for. Ideally what you would want to do is show a modal dialog with the spinner and an option to cancel, that way the user is blocked by the UI from interrupting the write process on the USB.
Even simply disabling the buttons during the write process could suffice.
Upvotes: 3
Reputation:
That's exactly what BackgroundWorker
is designed for. Although there are other ways to accomplish this, what you're doing is just fine.
Upvotes: 4