BackgroundWorker exception handling - c#

I'm working with the following components:
a Library (which throws an exception)
a test-console to test my logging
the enterprise library exception handling application blocks
the enterprise library logging application blocks
I'm invoking the library method by using a backgroundworker. The library throws the exception but the RunWorkerCompleted handler is never called.
The only way to catch the exception is to surround my DoWork handler code with a try/catch block.
Did is misunderstand the RunWorkerCompletedEventArgs.Error Property? Isn't it for getting exceptions which got caught by the BackgroundWorker?
Codesample:
static BackgroundWorker w = new BackgroundWorker();
w.DoWork += new DoWorkEventHandler(w_DoWork);
w.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(w_RunWorkerCompleted);
w.RunWorkerAsync();
static void w_DoWork(object sender, DoWorkEventArgs e)
{
MyClass m = new MyClass();
w.result = m.Compute();
}
static void w_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
HandleException(e.Error);
}
/* result related code */
}
static void HandleException(Exception e)
{
ExceptionPolicy.HandleException(e, "MyPolicy");
}
The above sample leads to a termination of my console application. The vs2010 output writes absolutely nothing (only default output).
So where's the problem?
//Edit: this snippet works for catching the library's exception.
static void w_DoWork(object sender, DoWorkEventArgs e)
{
try
{
MyClass m = new MyClass();
w.result = m.Compute();
}catch(Exception e){ }
}

That is the correct pattern for BackgroundWorker.
I suspect the problem is that your Main method is exiting before the BW has completed.
RunWorkerAsync will return immediately and if you are not waiting in Main, then your process will end, perhaps even before the BW has started, never mind completed.
Try adding a Console.ReadLine at the end of your Main method.
Out of interest:
BW behaves differently in a Console app and a Windows app. If you use a WinForms or WPF app, there will be a derived SynchronizationContext on your UI thread and BW will marshal RunWorkerCompleted back to the UI thread and run it there. That's one of the main benefits of BW.
In a Console App, the default SynchronizationContext is used and this marshals RunWorkerCompleted onto a thread pool thread. This means you can block the Main thread and the completed handler will still run.

Related

Why is 'RunWorkerCompleted' executed on the wrong thread?

In the following code, when the BackgroundWorker is launched, a SynchronizationContext does exist, but still, the RunWorkerCompleted handler is executed on a different thread than the RunWorkerAsync() and therefore throws an exception. Why?
And when the call to tempForm is removed it runs fine. (And the same for when substituting a MessageBox for a Form there.)
(The code shows a Form, launches a BackgroundWorker that references another Form f1 after one second, and then shows this second Form f1.)
public static Form1 f1;
static BackgroundWorker worker = new BackgroundWorker();
[STAThread]
static void Main()
{
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
f1 = new Form1();
using (Form1 tempForm = new Form1()) tempForm.ShowDialog();
//MessageBox.Show("A MessageBox won't cause the exception later. Only the Form does.");
if (SynchronizationContext.Current == null) throw new Exception("This is NOT thrown");
worker.RunWorkerAsync();
Application.Run(f1);
}
static void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show(f1, "Inside RunWorkerCompleted");
//Throws: Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on.
}
static void worker_DoWork(object sender, DoWorkEventArgs e)
{
Thread.Sleep(1000);
}
Can anyone please explain what is going on here?
The problem is because you call the RunWorkerAsync from a default synchronization context. As a small example:
public static void Main()
{
var ctx1 = SynchronizationContext.Current; // returns null
var form = new Form();
var ctx2 = SynchronizationContext.Current; // returns a WindowsFormsSyncContext
form.ShowDialog();
var ctx3 = SynchronizationContext.Current; // returns a SynchronizationContext
worker.RunWorkerAsync(); // wrong context now
}
It appears that instantiating a form associates a WindowsFormsSynchronizationContext with the current thread. Interestingly after closing the form the associated synchronization context will be set to the default one, i.e. the one that uses the threadpool.
After some digging I found the reason for the - at a first glance - strange behaviour: the constructor of Control initializes the WindowsFormsSynchronizationContext if necessary (see reference source). Once you return from ShowDialog then there won't be any message loop, so SynchronizationContext.Current has to be reset, in this case to the default threadpool SynchronizationContext.
The Windows UI is not thread safe and does not support multi-threading at all. For this reason there is a check as to which thread creates and later tries to manipulate the allocated graphics resources. To avoid the exception you MUST use the invoke pattern shown here:
if(InvokeRequired)
{
Invoke(worker_RunWorkerCompleted, sender, e);
}
else
{
MessageBox.Show(f1, "Inside RunWorkerCompleted");
}
The fact that a different thread runs the method is normal. The Windows Forms are constructed by the entrant thread which must be re-entrant, this means you should not block (infinitely loop) the thread that runs the program at first.
If you look closely, in Main() is a Run() method somewhere. This is done so that the creating thread is free to terminate while the form goes on living his own life on the desktop.

BackgroundWorker starts other threads that have an exception. Exception not caught?

I have a BackgroundWorker that I call from my main UI thread as follows.
On the MainWindow I declare the BackgroundWorker
private BackgroundWorker backgroundWorkerRefreshFromWeb = new BackgroundWorker();
And in the constructor I set it up as follows.
backgroundWorkerRefreshFromWeb.WorkerReportsProgress = true;
backgroundWorkerRefreshFromWeb.WorkerSupportsCancellation = true;
backgroundWorkerRefreshFromWeb.DoWork +=
new DoWorkEventHandler(backgroundWorkerRefreshFromWeb_DoWork);
backgroundWorkerRefreshFromWeb.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(backgroundWorkerRefreshFromWeb_RunWorkerCompleted);
backgroundWorkerRefreshFromWeb.ProgressChanged +=
new ProgressChangedEventHandler(backgroundWorker_ProgressChanged);
At the end of the constructor I call the following method to start it on its way.
private void RefreshWebDataTimer(Object state)
{
if (!backgroundWorkerRefreshFromWeb.IsBusy && !backgroundWorkerLoadFromDB.IsBusy)
{
System.Diagnostics.Debug.Print("Refresh Timer Started at {0}", DateTime.Now);
backgroundWorkerRefreshFromWeb.RunWorkerAsync(nfl);
}
}
The DoWork event handler calls a method from another project (DLL). That method has a pattern which calls multiple threads to do process work. when one of those threads throws an error, the application crashed and the BackgroundWorker does not catch it in the RunWorkerCompleted event. The pattern is a complex (probably overly complicated) but is as follows.
In the method the DoWork event handler calls I create a set of "sub-worker" threads in a wrapper as follows...and then WAIT for all threads to finish processing before moving on.
private static void GetRoster(Nfl teams, ref ManualResetEvent[] mre, ref int index)
{
mre = new ManualResetEvent[Dictionary.NUMBER_OF_NFL_TEAMS];
ParseDataAsyncWrapper[] dw = new ParseDataAsyncWrapper[Dictionary.NUMBER_OF_NFL_TEAMS];
index = 0;
foreach (NflTeam team in teams)
{
//Get Roster and create players
mre[index] = new ManualResetEvent(false);
dw[index] = new ParseDataAsyncWrapper(team, mre[index]);
ThreadPool.QueueUserWorkItem(new WaitCallback(dw[index].RosterCallbackAsync), index++);//Don't fully understand this
Thread.Sleep(wait.Next(Dictionary.THREAD_WAIT_MS));
}
foreach (ManualResetEvent re in mre) { if (re != null) { re.WaitOne(); } } //Wait for all threads to finish
mre = null; //allow to be disposed
dw = null;
}
I use the callback for each thread to get a webpage and then process that page:
internal async void RosterCallbackAsync(object State)
{
if (Thread.CurrentThread.Name == null) { Thread.CurrentThread.Name = string.Format("Roster{0}", State); }
WebPage = await Html.WebClientRetryAsync(Dictionary.ROSTER_WEBPAGE.Replace(Dictionary.CITY_REPLACE_STR, this.Team.CityAbr));
Html.ParseRoster(WebPage, Team);
DoneEvent.Set();
}
I am then throwing the exception in Html.ParseRoster but it is not getting caught. This is on a different thread than the BackgroundWorker. I don't know why the BackgroundWorker is not catching it. Since I am waiting for all threads to finish before moving on I don't think the RunWorkerCompleted event would run before I am done here.
I've looked at the help page for Application.DispatcherUnhandledException event and it states:
you will need to write code to do the following: Handle exceptions on
the background thread. Dispatch those exceptions to the main UI
thread. Rethrow them on the main UI thread without handling them to
allow DispatcherUnhandledException to be raised.
My question is 1) Why is the excpetion not being caught? Should I use Application.DispatcherUnhandledException and if so how can I accomplish this? I would ultimately like to throw these exceptions to the BackgroundWorker. Any suggestions or comments would be greatly appreciated.
UPDATE
I have been working on using TPL with await/async and Tasks and updated my code. This has been somewhat successfull as I am now getting the exception back to the BackgroundWorker. Ignoring how I get the exception back to the DoWork event for now ... I check that I am getting an exception by adding a try/catch block and am catching and re-throwing the exception. Here is my DoWork event
private async void backgroundWorkerRefreshFromWeb_DoWork(object sender, DoWorkEventArgs e)
{
// Do not access the form's BackgroundWorker reference directly.
// Instead, use the reference provided by the sender parameter.
BackgroundWorker bw = sender as BackgroundWorker;
// Start the time-consuming operation.
NflStatsComplete = false;
bw.ReportProgress(0, "Starting Data Refresh from Web...");
try
{
e.Result = await Html.RetrieveWebData(bw, e);
}
catch (Exception ex)
{
throw;
}
// If the operation was canceled by the user,
// set the DoWorkEventArgs.Cancel property to true.
if (bw.CancellationPending)
{
e.Cancel = true;
}
}
In the debugger I get an exception and see it thrown. However, when it then goes to the RunWorkerCompleted event the RunWorkerCompletedEventArgs e shows e.Error == null. I don't understand how this can be since I am throwing an exception directly from the DoWork event. Can someone explain this behavior?
The Backgroundworker is setup as an async method. I believe this is causing the RunWorkerCompleted event to fire before the DoWork event has completed and the exception has been raised. By updating the DoWork method to remove the async compiler keyword and removing any awaits the exception propogates back to the method specified by the RunWorkerCompleted event and e.Error != null

C# XAML GUI Freezes Because of API Function

I have a program that has stock quotes pushed to me via an API. The program also has a front end, made in XAML, that freezes while this program is running (i.e. processing the information that the API is sending me). I've tried using Dispatcher.Invoke and/or BackgroundWorker and have read up on threading plenty, but can't get it to unfreeze. Perhaps I'm just doing something wrong. I've attached the relevant code here. Was hoping someone could help.
private void QuoteUpdate(QuoteInfo info)
{
BackgroundWorker bwQuoteUpdate = new BackgroundWorker();
bwQuoteUpdate = new BackgroundWorker();
bwQuoteUpdate.WorkerSupportsCancellation = true;
bwQuoteUpdate.DoWork += bwQuoteUpdate_DoWork;
bwQuoteUpdate.RunWorkerAsync(info);
}
private void bwQuoteUpdate_DoWork(object sender, DoWorkEventArgs e)
{
try
{
Dispatcher.Invoke(DispatcherPriority.Normal, new ThreadStart(() =>
{
QuoteInfo info = e.Argument as QuoteInfo;
//logical functions and work are here
}));
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show("Error in QuoteUpdate: " + ex.Message, "Exception Thrown");
}
}
Although you’re creating a BackgroundWorker with the intention of executing your long-running task on a background thread, you’re still dispatching all your processing back onto the UI thread.
private void bwQuoteUpdate_DoWork(object sender, DoWorkEventArgs e)
{
// Code here runs on background thread.
Dispatcher.Invoke(DispatcherPriority.Normal, new ThreadStart(() =>
{
// Code here runs on UI thread.
}));
}
What you need to do is first perform your calculations on the background thread, but do not update any UI components; rather, store all your results in local variables. Then, once that’s done, use the Dispatcher.Invoke to dispatch control back to the UI thread, and use the results stored in your local variables to update your UI.
For example:
private void bwQuoteUpdate_DoWork(object sender, DoWorkEventArgs e)
{
// Code here runs on background thread.
QuoteInfo info = e.Argument as QuoteInfo;
string result = PerformLongRunningProcessing(info);
Dispatcher.Invoke(DispatcherPriority.Normal, new ThreadStart(() =>
{
// Code here runs on UI thread.
this.resultTextBox.Text = result;
}));
}
Yes, you are doing something wrong. The computation should be done in thread alone add only UI changes should be done in Dispatcher.Invoke.
And if you use DataBinding through INotifyPropertyChange, then drop the Dispatcher.Invoke completly, because marshaling the changes to UI thread is done automaticaly.
Try
Dispatcher.BeginInvoke(...)

Theads to keep alive a process but useless otherwise

Hello guys I have a question regardless a old code a client needed a update.
This code add a thread.sleep(500) to keep the service alive, is reading from a com port some calls, and sending a alarm to other pcs now this time when I was sending some information to the machine in question this error pops out
Cross-thread operation not valid: Control 'textBox1' accessed from a thread other than the thread it was created on.
private void port_DataReceived(object sender, SerialDataReceivedEventArgs e) {
Thread.Sleep(500);
string data = port.ReadExisting();
//try
//{
if (textBox1.TextLength == 30000)
{
textBox1.Text = "";
}
//}
//catch (Exception) {}
this.BeginInvoke(new setTextDeleg(si_DataRecived), new object[]{
data});
}
This is the function that writes on the com machine, does making a exception to eat the error is ok, or is there another better way to handle it?
PD: Sorry for my bad english, this is on C# 2008 vs
You should modify GUI components like textboxes and labels only from the thread that created them which is the main thread. You may take a look at BackgroundWorker which simplifies this task in WinForms application. And here's another useful article illustrating the usage of the InvokeRequired property and the Invoke method.
It's not a good idea to simply swallow this exception. The exception is occurring because you are not allowed to modify UI components from any thread other than the UI thread (the thread that created them). Instead, check out this MSDN article on how to pass information between worker threads (your thread that sleeps) and UI threads to update the text box in the correct manner.
The problem is because Windows Forms Controls are not thread-safe, and it would seem that the control is not being invoked properly for a thread-safe call. You can use the BackgroundWorker class or you can invoke it yourself. Here is a small code example.
// Delegate used by our worker thread to invoke our control
private delegate void ProgressDelegate(int value);
// Callback method used for our delegate
private void ProgressCallback(int value) {
progressBar1.Value = value;
}
protected override void OnShown(EventArgs e) {
Thread thread = new Thread(new ThreadStart(MyThreadWorker));
thread.IsBackground = true;
thread.Start();
}
// Thread method
private void MyThreadWorker() {
// Setup the delegate
ProgressDelegate mydelegate = new ProgressDelegate(ProgressCallback);
// Do some work
int pos = 0;
do {
pos++;
// Make a thread-safe call to our control and invoke our callback on the original thread
// Original thread: The thread the form and control were created on
progressBar1.Invoke(mydelegate, pos);
} while (pos < 100);
}
I'm guessing what some of your other code looks like, but you could probably move this
if (textBox1.TextLength == 30000)
{
textBox1.Text = "";
}
to the si_DataRecived method, so that it gets executed as part of the BeginInvoke call, the target of which will execute on the main (UI) thread.

C# threading issue

To play a bit with threading, delegates and backgroundworkers, I'm putting together a few small applications, I'm having a bit of trouble with one of them.
I've a Windows form, with a textbox, a button and a richttext.
When I press the button, the text in the textbox is used as a paramter to instantiate a class, like this:
public partial class Form1 : Form
{
private BackgroundWorker backgroundWorker;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += new DoWorkEventHandler(worker_DoWork);
backgroundWorker.RunWorkerAsync();
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
new Thread((ThreadStart)delegate()
{
this.BeginInvoke((ThreadStart)delegate()
{
foreach (string line in textBox1.Lines)
{
Dig digger = new Dig(line, textBox1.Text);
digger.DomainChecked += new Dig.DomainCheckedHandler(OnUpdateTicker);
string response = digger.GetAllInfo();
richTextBox1.AppendText(response);
Application.DoEvents();
}
});
}).Start();
}
void OnUpdateTicker(string msg)
{
new Thread((ThreadStart)delegate()
{
this.BeginInvoke((ThreadStart)delegate()
{
label4.Text = msg;
Application.DoEvents();
});
}).Start();
}
}
When debugging I run into a 'textBox1.Lines' threw an exception of type 'Microsoft.VisualStudio.Debugger.Runtime.CrossThreadMessagingException'
Any tips on how to solve this problem?
First, there is no need to create new threads inside DoWork; the whole idea with the BackgroundWorker is that DoWork is executed on a separate thread. Second, since DoWork is executed on a separate thread and UI controls can be modified only on the UI thread, you need to invoke those updates correctly. So, a rewritten version of worker_DoWork could look like this:
void worker_DoWork(object sender, DoWorkEventArgs e)
{
foreach (string line in textBox1.Lines)
{
Dig digger = new Dig(line, textBox1.Text);
digger.DomainChecked += new Dig.DomainCheckedHandler(OnUpdateTicker);
string response = digger.GetAllInfo();
richTextBox1.Invoke((Action) delegate { richTextBox1.AppendText(response); });
}
}
Note how the code does not explicitly spawn any new threads, and also how the AppendText method call is done through a Control.Invoke call, forcing it to execute on the UI thread.
The main reason is that the textbox is not owned by the background thread.
Your UI thread owns all the UI objects, and you're spinning up a background thread when a button is pressed. That background thread should not have access to any UI objects.
If you want the value of the textbox to be used, you'll need to pass it to your background thread another way.
Have a look here for an explanation (and solution).
You can only update controls on the main thread from the main thread itself, unless you explicitly tell your program that it's ok to do, by using the .Invoke method of the control.
From: http://www.albahari.com/threading/part3.aspx
Control.Invoke
In a multi-threaded Windows Forms application, it's illegal to call a method or property on a control from any thread other than the one that created it. All cross-thread calls must be explicitly marshalled to the thread that created the control (usually the main thread), using the Control.Invoke or Control.BeginInvoke method. One cannot rely on automatic marshalling because it takes place too late – only when execution gets well into unmanaged code, by which time plenty of internal .NET code may already have run on the "wrong" thread – code which is not thread-safe.

Categories

Resources