Background worker doesn't catch errors from ThreadPool - c#

I'm using a BackgroundWorker since I need to give a feedback in my UI. I also need to use ThreadPool in my class for it to run asynchronously but it seems that my background worker cannot catch the exception it throws.
It's intended to give a feedback on what task it's doing.
I made a simple application which reproduces the problem :
// MAIN UI CLASS
BackgroundWorker _bgWorker = new BackgroundWorker();
void _bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
// Returns custom error
MessageBox.Show(e.Error.Message);
}
}
void _bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
try {
// Initialize test error class
var testClass = new TestClass();
}
catch (Exception ex)
{
MessageBox.Show("im from trycatch");
}
}
private void Window_ContentRendered(object sender, EventArgs e)
{
_bgWorker.DoWork += _bgWorker_DoWork;
_bgWorker.RunWorkerCompleted += _bgWorker_RunWorkerCompleted;
}
/////////////////////////////////////////////////////////////////////////
// TEST ERROR CLASS
public TestClass()
{
throw new Exception("im a custom error");
}
This works all right. With the try-catch, it catches the error as handled by the try-catch. Whereas, without the try-catch, it handles it in RunWorkerCompleted.
However, I really need to do these methods asynchronously (which is why I use ThreadPool) but the debugger always points at the exception. (it only crashes the program when I try it without the debugger)
// TEST ERROR CLASS
public TestClass()
{
ThreadPool.QueueUserWorkItem(delegate
{
throw new Exception("im a custom error");
}
}
I tried to rethrow the error using a try-catch statement (hoping it would pass the exception to the BackgroundWorker) but it didn't work.
Hope you can help me.
Thanks in advance.

You do not need to put code on the ThreadPool from the DoWork method. The BackgroundWorker will raise your _bgWorker_DoWork method on a ThreadPool thread automatically when you call RunWorkerAsync().
If you are trying to start SEVERAL MORE ThreadPool tasks from your background worker, then I suppose that could be a consideration, but in that case I would suggest using the TPL in .NET4 if at all possible...
http://msdn.microsoft.com/en-us/library/dd460717(v=vs.110).aspx
If you can not use TPL, then you must wrap your ThreadPool code in a TryCatch and put the Exception some place that can be accessed outside of that method.
For example add a internal static List<Exception> backgroundExceptions; member to your UI class, the background worker add's any caught exceptions to this list, and then you must investigate this list after your background work has completed to see if there were any Exceptions present.
This is essentially what the TPL is doing for you ... giving you a place to 'observe' exceptions that happened on the background thread, but it is still your responsibility to look for those Exceptions, either in the Task.Exception property, or by calling a method that will re-throw the Exception for you.

Related

Is there any way to avoid thread pool from crashing when one thread gets exception?

This question for .NET framework. Putting a try/catch block in the call method is a solution for this problem but I want to handle it in upper level. Is there any solution for this?
You can handle in upper level, but you still need to put try catch inside the call. However, instead of handling the exception inside the catch - you would add the exception into a ConcurrentQueue and then outside the call you can throw an AggregateException with the List<Exception> that has been queued throughout the execution of your multithreaded code.
Here is an example for how it is implemented when using Parallel.ForEach https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/how-to-handle-exceptions-in-parallel-loops
No. Don't let exceptions hit the top of stacks, basically. Ever.
You can use an asynchronous delegate. Any exception not caught in the worker thread will be thrown in the main thread when you call EndInvoke().
public class Program
{
public static void DoParallelStuff(string caller)
{
for (var i = 1; i<5; i++)
{
Console.WriteLine("I'm doing stuff for {0} on thread {1}....{2}", caller, Thread.CurrentThread.ManagedThreadId, i);
Thread.Sleep(100);
}
}
public static void Background()
{
DoParallelStuff("Background");
Console.WriteLine("Background will now throw an exception.");
var a = ((string)null).ToString();
}
public static void Main()
{
try
{
Action background = Background;
var result = background.BeginInvoke(null, null);
DoParallelStuff("Foreground");
background.EndInvoke(result);
}
catch(System.Exception exception)
{
Console.WriteLine("This was caught in the main thread: '{0}'", exception.Message);
}
}
}
Output:
I'm doing stuff for Foreground on thread 238....1
I'm doing stuff for Background on thread 459....1
I'm doing stuff for Background on thread 459....2
I'm doing stuff for Foreground on thread 238....2
I'm doing stuff for Foreground on thread 238....3
I'm doing stuff for Background on thread 459....3
I'm doing stuff for Background on thread 459....4
I'm doing stuff for Foreground on thread 238....4
Background will now throw an exception.
This was caught in the main thread: 'Object reference not set to an instance of an object.'
Try it on DotNetFiddle

C# Is it safe to call 'BackgroundWorker' inside 'using' statement?

I have developed a windows service. In the service I was using a BackgroundWorker to Post data in my Database.
I declared a BackgroundWorker inside my database constructor class and was using that whenever needed.
During the test I got one error:
This BackgroundWorker is currently busy and cannot run multiple tasks
concurrently
I tried to find out the solution and many people suggest to use new instance for each task. I changed my code like:
...
using (BackgroundWorker bw = new BackgroundWorker())
{
bw.DoWork += new DoWorkEventHandler(bkDoPost);
bw.RunWorkerAsync(dbobj);
}
...
and my 'bkDoPost' is:
void bkDoPost(object sender, DoWorkEventArgs e)
{
try
{
dbObject dbj = e.Argument as dbObject;
this.db.Insert(dbj.tableName, dbj.data);
}
catch (Exception ex)
{
logs.logMessage("There was an error in data post. See the ErrorLog");
logs.logError(ex);
}
}
The code works fine during test.
My question is am I doing correct way?
OR Is there any issue doing in that way?
Thanks
Don't do that. Your background worker will be disposed before your work completes.
It is better to call Dispose manually after the work completes.
Better still, consider using a different scheme for handling asynchronous work. Background worker is becoming obsolete and is targeted at UI applications, rather than services. The restriction on parallel operations highlights the intention of the class.
Don't put the BackgroundWorker into a using statement. Instead put the Dispose() call into the RunWorkerCompleted event.
Nevertheless BackgroundWorker is maybe not the best thing to use in your case, cause it is primilary use is to run some buisness code while the UI stays responive and to automatically update the UI within the RunWorkerCompeleted event.
If you don't need to interfere with the UI when the job is finished or you have a lot of smaller jobs to be done it would be more efficient to switch to encapsulate your jobs within Tasks.
If you have many updates, creating one BackgroundWorker for each one could be very time and memory consuming.
I would use an independant thread that I would wake up each time an update has to be done :
Queue<DbWork> dbUpdates = new Queue<DbWork>();
EventWaitHandle waiter = new EventWaitHandle(false, EventResetMode.ManualReset);
...
// Init :
new Thread(new ThreadStart(DbUpdateWorker));
...
private void DbUpdateWorker()
{
while (true)
{
DbWork currentWork = null;
lock (dbUpdates)
{
if (dbUpdates.Count > 0)
currentWork = dbUpdates.Dequeue();
}
if (currentWork != null)
{
currentWork.DoWork();
}
if (dbUpdates.Count == 0)
{
waiter.WaitOne();
}
}
}
public void AddWork(DbWork work)
{
lock (dbUpdates)
{
dbUpdates.Enqueue(work);
}
waiter.Set();
}

Multiple background threads

I have one c# application that uses BackGroundWorker to do a group of tasks:
private void buttonStartCheckOut_Click(object sender, EventArgs e)
{
BackgroundWorker checkOuter = new BackgroundWorker();
checkOuter.DoWork += new DoWorkEventHandler(checkOuter_DoWork);
checkOuter.RunWorkerAsync();
checkOuter.RunWorkerCompleted += new RunWorkerCompletedEventHandler(checkOuter_RunWorkerCompleted);
}
void checkOuter_DoWork(object sender, DoWorkEventArgs e)
{
if (textBoxCICheckOut.Text != "")
CheckOutCI();
if (textBoxCACheckOut.Text != "")
CheckOutCA();
if (textBoxCAuthCheckOut.Text != "")
CheckOutCAuth();
if (textBoxCLCheckOut.Text != "")
CheckOutCL();
if (textBoxCCCheckOut.Text != "")
CheckOutCC();
}
As you can see, I have only 2 threads; one for GUI and one for secondary task.
Its easy for me to track when all the functions finish.
Now I want to make it more fast by creating a separate thread for CheckOutCI(), CheckOutCA() and others.Creating 5 background workers looks kinda dirty.
I want to ask:
How will I keep track of when all the functions have finished executing.
If any one function returned an exception, I want to display it to user and ask the user to correct the user and try again. I hope I am able to explain my question properly.
PLEASE edit the code by wdavo as per my comment on his post.
I'd look at using the Task library (Assuming you are running .NET 4.5 or later). I find it much better to use than background workers in most cases.
(Note you can still use the Task library in .NET 4, however Task.WhenAll is only available in 4.5)
http://msdn.microsoft.com/en-us/library/dd235618
Without rewriting your whole program, here's an example of how you would use it:
Move your simple conditional logic to the button
private void button1_Click(object sender, EventArgs e)
{
var tasks = new List<Task>();
if (Text == "A")
{
tasks.Add(funcA());
}
if (Text == "B")
{
tasks.Add(funcB());
}
//And so on....
Task.WhenAll(tasks.ToArray()).ContinueWith(t =>
{
if (t.Exception != null)
{
//One of the tasks threw an exception
MessageBox.Show("There was an exception!");
}
else
{
//None of the tasks threw an exception
MessageBox.Show("No Exceptions!");
}
});
}
We add the tasks to a collection so we can know when they all finish via Task.WhenAll. When all the tasks in the collection have finished, a message box will be displayed. If any of the tasks in the collection have thrown an exception, the Exception property of 't' will be populated. The specific exceptions exist as inner exceptions of this exception.
Move your threading code to individual task/functions. You'd create your checkout functions to look similar to this:
private Task funcA()
{
return Task.Factory.StartNew(() =>
{
try
{
//Code running here will be executed on another thread
//This is where you would put your time consuming work
//
//
}
catch(Exception ex)
{
//Handle any exception locally if needed
//If you do handle it locally, make sure you throw it again so we can see it in Task.WhenAll
throw ex;
}
//Do any required UI updates after the work
//We aren't on the UI thread, so you will need to use BeginInvoke
//'this' would be a reference to your form
this.BeginInvoke(new Action(() =>
{
//...
}));
});
}
What this does is the following
Creates a and runs a task which does some work on a thread from the thread pool
If there is an exception, we handle it locally .We re-throw the exception so that we can know that a task has failed when 'Task.WhenAll' is executed
Updates the UI after the work is done. You need to call BeginInvoke to run the code on the UI thread to avoid cross threading issues.
Starting up more threads than CPUs or cores can actually make your application slower. When there are more CPU-bound threads than CPUs the OS needs to context switch more often between the threads--which is hugely expensive and could result in the OS spending more time context switching between your threads than giving them time to work.
You can use the parallel aspect of the Parallel Task Library to automatically distribute your load across CPUs. For example:
Action[] actions = new Action[] {CheckOutCI, CheckOutCA, CheckOutCAuth, CheckOutCL, CheckOutCC};
Parallel.ForEach(actions, e=>e());
...which isn't exactly what you want; but should give you a general idea. e.g. populate actions based on current conditions.
You need to use ReportProgress method in backgroundworker
void checkOuter_DoWork(object sender, DoWorkEventArgs e)
{
if (textBoxCICheckOut.Text != "")
CheckOutCI();
checkOuter.ReportProgress(completionPercentage,"Error message");
The data sent in ReportProgress can be captured in checkOuter_ProgressChanged event
checkOuter_ProgressChanged(object sender,ProgressChangedEventArgs e)
{
int percentage = e.ProgressPercentage;
string message = e.UserState;
}

Catch child background threads within calling method in foreground thread

I want to call a method once by a specified interval (e.g. 3 seconds), but I have problem catching exception within the calling method (e.g. Start) thrown by the called method (timer_Elapsed)
Update
public void Start()
{
Timer timer = new Timer(PingPeriod); //System.Timers
try
{
timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
timer.Enabled = true;
}
catch (Exception ex)
{
//I want to catch exception thrown by timer_Elapsed
}
//Logic here that keeps the method running
}
private void timer_Elapsed(object sender, ElapsedEventArgs e)
{
throw new exception("Catch this exception please");
}
Update:
Can anyone please confirm that the start() will NOT catch exception thrown from timer_Elapsed, or when trying to invoke timer_Elapsed if exception occurred.
Is there a better alternative, which can catch exception from a called method or get notified?
Any idea would be very much appreciated!
This just isn't possible, the Elapsed event handler runs later, well after your Start() method has exited. Furthermore, that event has the nasty habit of swallowing all exceptions without any diagnostic.
You'll need to handle exceptions within the event handler. Certainly not easy to do. Do consider using the System.Threading.Timer class instead. At least your program will terminate when the callback throws an exception.
This is not possible because the exception is thrown on a different execution context (thread) from the one that set up the timer.
You should handle any exceptions in your callback method. If you want your logic separated from your exception handling, simply create a separate method.
private void TimerElapsedCallback(object sender, ElapsedEventArgs e)
{
try
{
this.DoSomething();
}
catch (Exception ex)
{
// handle
}
}
private void DoSomething()
{
// logic goes here and can be agnostic of any exceptions it throws, if desired
}

BackgroundWorker dies unexpectedly

I have the following code:
public Mainform()
{
...
// scheduler
scheduler.DoWork += new System.ComponentModel.DoWorkEventHandler(scheduler_DoWork);
scheduler.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(scheduler_RunWorkerCompleted);
scheduler.WorkerReportsProgress = false;
scheduler.WorkerSupportsCancellation = true;
...
...
scheduler_DoWork(this, null);
scheduler.RunWorkerAsync(1000);
...
}
void scheduler_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
scheduler_Enabled = false;
CustomExceptionHandler eh = new CustomExceptionHandler();
eh.HandleUnhandledException(e.Error, "scheduler");
}
if(scheduler_Enabled)
{
scheduler.RunWorkerAsync(1000);
}
}
void scheduler_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
try
{
try
{
...do some stuff
}
catch(MyException ex)
{
ThreadSafeShowError();
}
finally
{}
...do more stuff
}
finally
{
if (e != null && e.Argument != null)
{
Thread.Sleep((int)e.Argument);
}
}
}
The backgroundworker thread died unexpectedly without any exception being thrown. I did not encounter this problem during development and it seems to be hard to reproduce. I suspected that maybe a cross thread exception was occurring when I am doing work in the scheduler_DoWork. I have tried to explicitly update the UI without checking if InvokeRequired and the thread continues to run without problems in a release build. How can this be? (Cross thread exception should occur) How can I determine what causes the thread to die? Any suggestions on how to figure out what is going wrong or how to debug this issue will be appreciated?
The RunWorkerCompleted event might not be fired on the UI Thread. If it is not, then the thread will end and your scheduler object will be garbage collected, which will make it seem like it just quit with no error. See this post for more details. Here and here are SO posts about this.
Your sample doesn't show enough code to determine what's going on but:
Maybe an exception is being thrown from ThreadSafeShowError? Why are you trying to show an error from the worker thread anyway - the conventional thing to do is to show e.Error if not null in the RunWorkerCompleted event handler.
To debug the issue try putting the following around all the code in your DoWork handler:
try
{
// do work
// log a trace statement here
}
catch(Exception ex)
{
// log exception, e.g. with System.Diagnostics.Debug.Write
throw;
}
finally
{
// log a trace statement here
}
You can do several things to increase the possibility of catching the exception:
Enable Managed Debugging Assistants for all exceptions in VS. To do that, go to Debug menu -> Exceptions..., and put a check mark next to "Managed Debugging Assistants" to enable all exceptions to be caught using debugger. Also, depending on the code you are executing, expand the CLR Exceptions node and select nodes of interest ("System" node, for example, will catch all exceptions from the System namespace in the debugger).
Obviously, put a try/catch block around your code, with some logging. You can also do something like this, if you are in real trouble:
try
{
// do stuff
}
catch (Exception ex)
{
#if DEBUG
// break only in DEBUG builds
System.Diagnostics.Debugger.Break();
#endif
// log the exception and throw
throw;
}
Put a try/catch with logging around your Application.Run(...) code in the Main() method. Exceptions do propagate up there sometimes, and this can catch them (even if not coming from this specific part of your code).
Add an Application.ThreadException event handler in your Main() method, before calling Application.Run, like this:
Application.ThreadException +=
new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
In Mainform, you never call scheduler.RunWorkerAsync, so your BackgroundWorker does not start at all.

Categories