BackgroundWorker completes before DoWork - c#

I'm using a background worker to handle the loading of a file to stop my ui from freezing however it seems that the RunWorkerCompleted is finishing before my DoWork event has completed (Causes errors when exiting dialog)... is there anything I'm doing wrong? Am I better off doing this over a task?
public static <T> LoadDesign(string xmlPath)
{
PleaseWait pw = new PleaseWait(xmlPath);
pw.ShowDialog();
return pw.design;
}
private PleaseWait(string xmlFile)
{
InitializeComponent();
bw = new BackgroundWorker();
bw.WorkerSupportsCancellation = true;
bw.DoWork += (s, e) =>
{
design = (Cast)DllCall containing XmlSerializer.Deserialize(...,xmlFile);
};
bw.RunWorkerCompleted += (s, e) => {
//Exit please wait dialog
this.Close();
};
if (!bw.IsBusy)
bw.RunWorkerAsync();
}
I believe the issue may be down to the fact that my background worker is calling a dll and not waiting for the response. I've tried to add checks such as while(design == null) to no avail..
Edit2
The error is NRE as the design hasn't been loaded, I can easily fix this but would rather get the threading working instead.

There are lots of little mistakes. Given that we are probably not looking at the real code and that we don't have a debugger with a Call Stack window to see where it actually crashes, any one of them might be a factor.
Testing bw.IsBusy and not starting the worker when it is true is a grave mistake. It can't ever be busy in the code as posted but if it actually is possible for it to be true then you've got a nasty bug in your code. Since you actually did subscribe the events on a busy worker. Now the RunWorkerCompleted event handler will run twice.
Using the Close() method to close a dialog is not correct. A dialog should be closed by assigning its DialogResult property. Not the gravest kind of mistake but wrong nonetheless.
There's a race in the code, the worker can complete before the dialog is ever displayed. A dialog can only be closed when its native window was created. In other words, the IsHandleCreated must be true. You must interlock this to ensure this can never happen. Subscribe the dialog's Load event to get the worker started.
You blindly assume that the worker will finish the job and produce a result. That won't be the case when its DoWork method died from an exception. Which is caught by BackgroundWorker and passed to the RunWorkerCompleted event handler as the e.Error property. You must check this property and do something reasonable if it isn't null.
Judging from the comments, I'd guess at the latter bullet being the cause. You debug this by using Debug + Exceptions, tick the Thrown checkbox for CLR exceptions. The debugger will now stop when the exception is thrown, allowing you to find out what went wrong.

It maybe possible your background worker actually does not take much time and complete before the dialog is shown. I'd suggest shift the background worker initialization and start up code to PleaseWait's Form_Load or Form_Shown

If you Call another async function in your BackgroundWorker _DoWork event,
like;
private void BackgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
somethingsToDoAsync();
// somethingsToDoAsync() function is to ASYNC
}
_RunWorkerCompleted fires even before completed _Dowork event.
Change other somethingsToDoAsync() function to not async.

Related

Do you need two seperate InvokeRequired checks when working with 2 forms?

Given
Action closeLoadingAction = new Action(() =>
{
loadingForm.Close();
#region - may need to put into second Action
panelOnMainForm.Controls.AddRange(physdocControls.ToList<Control>().ToArray());
if (Handle != IntPtr.Zero)
User32DLL.SetForegroundWindow(this.Handle);//Handle is a property of the mainForm.
#endregion
});
Sometimes I get handle not created exceptions even though I check the Invoke required.
if(loadingForm.InvokeRequired)
loadingForm.Invoke(closeLoadingAction);
else
closeLoadingAction();
The loading form runs on the same thread as the main form. The code you see above runs in a seperate thread. I suspect that I need another check for invoke against the main form. Do I need a second check or is what I have already safe?
The code you see above runs in a seperate thread
Using InvokeRequired like this when you know that the code runs on another thread is a bad anti-pattern. You can do much more useful things with it. Like:
if (!loadingForm.InvokeRequired) {
throw new InvalidOperationException("Threading race detected");
}
loadingForm.Invoke(closeLoadingAction);
Or the more practical one if you meant to start the loading thread before the loading form's Load event fired:
while (!loadingForm.InvokeRequired) Thread.Sleep(50);
loadingForm.Invoke(closeLoadingAction);
Or just do it the right way and have the form's Load event start the thread.
You should only get those "handle not created" exceptions if the form hasn't been shown or was closed before you try to Invoke the delegate. Not enough code to determine if that's the case though, so if I were in your shoes I'd try to determine if the loading form's (since that's the invoker) Closed event is being called before the method actually gets invoked.
Because both forms are on the same thread, you should be safe just checking the loading form's InvokeRequired property. Though in general, if you're going to operate on the main form, you should check that form's InvokeRequired property. Likewise, if you're going to operate on the loading form (like calling Close above), you should check the loading form's InvokeRequired property. Above you're changing panelOnMainForm so I'd check that to be totally safe, but I don't believe it's necessary.
Sounds like a race between the handle being created for loadingForm and the call to loadingForm.Close().
One way around this is to start the thread that has your .Invoke/.Close code when the loadingForm.HandleCreated event fires.
private void loadingForm_HandleCreated(object sender, EventArgs e)
{
var t = new Thread(DoLoadingStuff);
t.Start();
}
private void DoLoadingStuff()
{
// ...
if(loadingForm.InvokeRequired)
loadingForm.Invoke(closeLoadingAction);
else
closeLoadingAction();
}

BackgroundWorker - CancellationPending changing to false in RunWorkerCompleted. Why?

After canceling the BackGroundWorker, in the DoWork, the CancellationPending is true but when he comes to the RunWorkerCompleted, the CancellationPending is false. I dont know what did I do wrong?
static BackgroundWorker b1;
static void Main(string[] args)
{
b1=new BackgroundWorker();
b1.DoWork += new DoWorkEventHandler(work1);
b1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(completed);
b1.WorkerSupportsCancellation = true;
b1.RunWorkerAsync("Hellow");
Console.ReadLine();
}
private static void completed(object sender, RunWorkerCompletedEventArgs e)
{
if (((BackgroundWorker)sender).CancellationPending)
Console.WriteLine("Canceled!");
else
Console.WriteLine("Result:" + e.Result);//it goes here every time
}
private static void work1(object sender, DoWorkEventArgs e)
{
((BackgroundWorker)sender).CancelAsync();
if (((BackgroundWorker)sender).CancellationPending)
{
e.Cancel = true;
}
}
By the way, How can I add an error that occur in the DoWork to the RunWorkerCompletedEventArgs.Error for shoing it up to the user?
Yes, the BackgroundWorker class sets the CancellationPending property to false before raising the RunWorkerCompleted event. Whether or not the worker was actually cancelled.
This is quite intentional, it stops you from falling into a nasty trap that's always around when you use threads. Code that uses threads often misbehaves randomly and unpredictably due to a kind of bug called "threading race". It is a very common kind of bug and dastardly difficult to debug.
What can easily go wrong in your intended approach if BGW didn't do this is that you'll assume that the worker got cancelled when you see CancellationPending set to true. But that's an illusion, you cannot tell the difference between it being cancelled and it completing normally. The corner case is you calling CancelAsync() a microsecond before the worker completes. The worker never has a chance to even see the CancellationPending flag set to true, it was busy finishing the last bits of the DoWork event handler method. That's a threading race, the worker raced ahead of your call and completed normally.
The proper hand-shake that avoids this bug is your worker setting e.Cancel to true when it sees the CancellationPending property set to true. And of course stopping what's its doing. Now it is reliable, the e.Cancelled property in the RunWorkerCompleted event handler is a copy of e.Cancel. So your code can now reliably tell you whether or not the worker saw the cancel request.
I believe the CancellationPending property is for use during the background operation (in your work1 method). It will tell the background worker that you have requested the background operation be canceled. Once the RunWorkerCompleted event is called, the background worker has done the work to cancel the request, and therefore the cancellation is no longer pending.
EDIT: the RunWorkerCompletedEventArgs has a Cancelled property that will tell you if the background operation was cancelled.
If you throw an exception from the DoWork method (work1 in your case), it should be caught by the BackgroundWorker and populate the Error property of the RunWorkerCompletedEventArgs.

BackgroundWorker DisconnectedContext

i have the folling code:
public static Emgu.CV.Capture _capture;
public static DispatcherTimer _timer;
_timer = new DispatcherTimer();
_timer.Interval = _settings.camera_interval;
_timer.Tick += ProcessFrame;
BacgroundWorker _bw = new BackgroundWorker
{
WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
_bw.DoWork += (s, e) =>
{
// Initialize the device in background
_capture = new Capture();
};
_bw.RunWorkerCompleted += (s, e) =>
{
_capture.SetCaptureProperty(CAP_PROP.CV_CAP_PROP_FRAME_HEIGHT,
_settings.camera_height);
_capture.SetCaptureProperty(CAP_PROP.CV_CAP_PROP_FRAME_WIDTH,
_settings.camera_width);
Brightness = _capture
.GetCaptureProperty(CAP_PROP.CV_CAP_PROP_BRIGHTNESS);
Contrast = _capture
.GetCaptureProperty(CAP_PROP.CV_CAP_PROP_CONTRAST);
// Get images from camera
_timer.Start();
};
_bw.RunWorkerAsync();
public override void CleanUp()
{
_timer.Stop();
_bw.Dispose();
if (_capture != null) _capture.Dispose();
}
the app work fine but when i close the app throw me: Message: Context0x23754b0' Disconnected. ... how to fix this problem?
This is a COM related error, it no doubt happens because you create the Capture object on the background thread. A COM object has thread affinity, once the thread that creates it stops running, the COM object is dead and cannot be used anymore. Trying to use it anyway produces the warning.
That this doesn't occur in the RunWorkerCompleted event handler is quite remarkable, this must be buried inside the OpenCV or Emgu plumbing in a non-obvious way. That certainly doesn't mean it couldn't occur some day. You'll need to re-think this, it doesn't make much sense to only create the object on the worker and have everything else run on the UI thread. Do everything on the worker, including the disposing. Or none of it.
I assume this would have something to do with your camera capture library and how it potentially uses unmanaged resources.
I'd start by commenting all the code out of your RunWorkerCompleted to see if the message still happens. If it doesn't, then it's caused by one or more of the GetCaptureProperty calls. I suspect it won't though.
I see in the documentation of Egmu.CV.Capture that there is a Capture.DisposeObject() method that talks about releasing the captured object. My guess is that after you instantiate _capture and you do what you need to do, you have to do a clean-up. I'd suggest that after your ProcessFrame finishes (or on exit of your application) that you try calling _capture.DisposeObject() to see if that cleans up and exits gracefully.
Edit:
If all else fails, the approach I would suggest is comment out as much of your code as you can to get to the point where you can exit the program without it throwing an Exception. Then, comment in parts of code until you can locate exactly what gets created or run that will eventually cause your exception on exit. Once you can localize that, you'll have a better idea how to fix it.

How to properly handle form closing when using background workers?

I am observing a strange bug in some of my code which I suspect is tied to the way closing a form and background workers interact.
Here is the code potentially at fault:
var worker = new BackgroundWorker();
worker.DoWork += (sender, args) => {
command();
};
worker.RunWorkerCompleted += (sender, args) => {
cleanup();
if (args.Error != null)
MessageBox.Show("...", "...", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
};
worker.RunWorkerAsync();
This code is executed in a method in a form, when a button is pressed.
command() is slow, it may take a few seconds to run.
The user presses a button which executes the code above to be executed. Before it is done, the form is closed.
The problem is that calling cleanup() sometimes raises ObjectDisposedException. I say "sometimes", because this never happens on my computer. If the form is closed before command() is done, the handler I registered for RunWorkerCompleted is not executed. On another computer, the handler is called once out of hundred times. On a coworker's computer, it's almost always called. Apparently, the probability of execution of the handler rises with the age/slowness of the computer.
First question:
Is this the expected behaviour of BakgroundWorker? I would not expect it to know anything about the form, as there is nothing I can see that ties the form "this" with "worker".
Second question:
How should I go about fixing that problem?
Possible solutions I'm considering:
Test if (!this.IsDisposed) before calling cleanup(). Is that enough, or can the form be disposed while cleanup is being executed?
Wrap the call to cleanup() in a try {} catch (ObjectDisposedException). I don't like that kind of approach too much, as I may be catching exceptions that were raised due to some other unrelated bug in cleanup() or one of the methods it calls.
Register a handler for IsClosing and delay or cancel closing until the handler for RunWorker Completed has run.
Additional information that may be relevant: code from command() will cause updates to be done to GUI objects in "this". Such updates are performed via calls to this F# function:
/// Run a delegate on a ISynchronizeInvoke (typically a Windows.Form).
let runOnInvoker (notification_receiver : ISynchronizeInvoke) excHandler (dlg : Delegate) args =
try
let args : System.Object[] = args |> Seq.cast |> Array.ofSeq
notification_receiver.Invoke (dlg, args) |> ignore
with
| :? System.InvalidOperationException as op ->
excHandler(op)
The exceptions you mentioned do not have any connection to BackgroundWorker, other than the fact that one thread (the worker) tries to access controls which have been disposed by another thread (the UI).
The solution I would use is to attach an event handler to the Form.FormClosed event to set a flag that tells you the UI has been torn down. Then, then RunWorkerCompleted handle will check to see if the UI has been torn down before trying to do anything with the form.
While this approach will probably work more reliably than checking IsDisposed if you are not disposing the form explicitly, it does not provide a 100% guarantee that the form will not be closed and/or disposed just after the cleanup code has checked the flag and found that it is still there. This is the race condition you yourself mention.
To eliminate this race condition, you will need to synchronize, for example like this:
// set this to new object() in the constructor
public object CloseMonitor { get; private set; }
public bool HasBeenClosed { get; private set; }
private void Form1_FormClosed(object sender, FormClosedEventArgs e) {
lock (this.CloseMonitor) {
this.HasBeenClosed = true;
// other code
}
}
and for the worker:
worker.RunWorkerCompleted += (sender, args) => {
lock (form.CloseMonitor) {
if (form.HasBeenClosed) {
// maybe special code for this case
}
else {
cleanup();
// and other code
}
}
};
The Form.FormClosing event will also work fine for this purpose, you can use whichever of the two is more convenient if it makes a difference.
Note that, the way this code is written, both event handlers will be scheduled for execution on the UI thread (this is because WinForms components use a single-threaded apartment model) so you would actually not be affected by a race condition. However, if you decide to spawn more threads in the future you might expose the race condition unless you do use locking. In practice I have seen this happen quite often, so I suggest synchronizing anyway to be future-proof. Performance will not be affected as the sync only happens once.

Delegates And Cross Thread Exception

Whenever i am updating UI in windows form using delegate it gives me cross thread exception
why it is happening like this?
is there new thread started for each delegate call ?
void Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
//this call delegate to display data
clsConnect(statusMsg);
}
protected void displayResponse(string resp)
{
//here cross thread exception occur if directly set to lblMsgResp.Text="Test";
if (lblMsgResp.InvokeRequired)
{
lblMsgResp.Invoke(new MethodInvoker(delegate { lblMsgResp.Text = resp; }));
}
}
The DataReceived event is always raised on a threadpool thread. You cannot update any UI control, you have to use Control.BeginInvoke(). There is no point testing InvokeRequired, it is always true.
A couple of things to keep in mind here:
Don't call Control.BeginInvoke for every single character or byte that you receive. That will bring the UI thread to its knees. Buffer the data you get from the serial port until you've got a complete response. Using SerialPort.ReadLine() usually works well, a lot of devices send strings that are terminated by a line feed (SerialPort.NewLine).
Shutting down your program can be difficult. You have to make sure to keep the form alive until the serial port stops sending. Getting an event after the form is closed will generate an ObjectDisposed exception. Use the FormClosing event to close the serial port and start a one second timer. Only really close the form when the timer expires.
Avoid using Control.Invoke instead of BeginInvoke. It can deadlock your program when you call SerialPort.Close().
Lots of ways to get in trouble. Consider using your own thread instead using DataReceived to avoid them.
Port_DataReceived is obviously an async event handler that is being raised by a thread on port monitoring component.
is there new thread started for each
delegate call ?
No, probably not. Your port monitoring component is running the poll on a background thread and the event is being raised from that thread, every time.
The point is that it is being called on a thread other than the UI, so you will need to use Control.Invoke and the patterns associated with it.
Consider this, (and read the post that may illuminate things for you)
void Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
//this call delegate to display data
UpdateTheUI(statusMsg);
}
private void UpdateTheUI(string statusMsg)
{
if (lblMsgResp.InvokeRequired)
{
lblMsgResp.BeginInvoke(new MethodInvoker(UpdateTheUI,statusMsg));
}
else
{
clsConnect(statusMsg);
}
}
With all of that said, I would be remiss if I didn't point out that the indirection is troubling.
Cross Thread exception happens when some none UI thread changes UI elements. Since UI elements should be changed only in the UI thread this exception is thrown. To help you understand why this happen you will have to publish you code.
Cross thread exception happens when some none UI thread changes the UI elements. To fix this use the Invoke method on the control itself. As an extra you can check InvokeRequired on the control before calling the Invoke method
See msdn

Categories

Resources