I've made an app that runs around the clock with three Backgroundworkers running in different intervals.
In their DoWork i do some Dispatcher.BeginInvoke so it updateds some charts. The problem is that its crashing during the night and I'm unsure why. I've wrapped the Dispatcher.BeginInvoke in try/catch, but since I'm invoking the UI thread, I'm thinking I maybe do the try catch INSIDE the Dispatcher.BeginInvoke instead.
Does it matter?
test if the EventArgs Error property is not null in the RunWorkerCompleted method
backgroundWorker.RunWorkerCompleted += (s, e) =>
{
if (e.Error != null)
{
// handle error
}
}
The try/catch will only work with the stuff that's happening in its current thread. If an error happened in another thread, the worker will finish but you don't know why unless you inspect the Error property.
A callback queued with Dispatcher.BeginInvoke is asynchronous. You should observe all exceptions inside the delegate you pass into Dispatcher.BeginInvoke, because they are not getting propagated anywhere outside it (except as Application.Current.DispatcherUnhandledException, AppDomain.CurrentDomain.UnhandledException events and as DispatcherOperation.Task.Exception property, see below). If they go unhanded, they will crash the app inside the core Dispatcher event loop on the UI thread.
This includes RunWorkerCompletedEventArgs.Error too. An exception thrown inside Dispatcher.BeginInvoke delegate will not be available there as Error, upon RunWorkerCompletedEvent event.
Here is a simple example illustrating the problem. Note how e.Error is null inside RunWorkerCompleted:
// UI Thread
// prepare the message window
var window = new Window
{
Content = new TextBlock { Text = "Wait while I'm doing the work..." },
Width = 200,
Height = 100
};
// run the worker
var dispatcher = Dispatcher.CurrentDispatcher;
var worker = new BackgroundWorker();
worker.DoWork += (s, e) =>
{
// do the work
Thread.Sleep(1000);
// update the UI
dispatcher.BeginInvoke(new Action(() =>
{
throw new ApplicationException("Catch me if you can!");
}));
// do more work
Thread.Sleep(1000);
};
worker.RunWorkerCompleted += (s, e) =>
{
// e.Error will be null
if (e.Error != null)
MessageBox.Show("Error: " + e.Error.Message);
// close the message window
window.Close();
};
// start the worker
worker.RunWorkerAsync();
// show the modal message window
// while the worker is working
window.ShowDialog();
To solve the problem, observe the exceptions with something like this:
var step = 0; // progress
// do the work on a background thread
// ..
var lastStep = step++;
Dispatcher.BeginInvoke(new Action(() =>
{
try
{
// do the UI update
}
catch(Exception ex)
{
// log or report the error here
MessageBox.Show("Error during step #" +
lastStep + ": " + ex.ToString());
}
}));
Alternatively, you can keep track of all DispatcherOperation returned by Dispatcher.BeginInvoke:
var invokes = new List<DispatcherOperation>();
// do the work on a background thread
// ..
invokes.Add(Dispatcher.BeginInvoke(new Action(() =>
{ /* update the UI */ }))));
Then you can examine DispatcherOperation.Task.Exception of each invocation you've queued with Dispatcher.BeginInvoke. I don't think this is feasible though, unless you can prevent the invokes list from growing endlessly.
Related
I have a BackgroundWorker called bgw to whom I pass my custom Form called LogBoxForm to. The custom Form job is simply to print something on it.
LogBoxForm logBox = new LogBoxForm(); //both declared in the main Form
BackgroundWorker bgw = new BackgroundWorker();
In the main Form's Load event initiated the two bgw events: DoWork and RunWorkerCompleted like this
bgw.DoWork += bgw_DoWork;
bgw.RunWorkerCompleted += bgw_RunWorkerCompleted;
And then when I pressed a Button named button9, the bgw will be ran as directed by this code
//Background worker
BackgroundWorker bgw = new BackgroundWorker();
private void button9_Click(object sender, EventArgs e) {
if (bgw.IsBusy)
return;
bgw.RunWorkerAsync(logBox);
}
void bgw_DoWork(object sender, DoWorkEventArgs e) {
LogBoxForm lbf = e.Argument as LogBoxForm;
try {
for (int i = 0; i < 5; ++i) {
lbf.WriteTimedLogLine("loop " + (i + 1).ToString());
Thread.Sleep(1000);
}
} catch (Exception exc) {
throw exc;
}
}
void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
logBox.WriteTimedLogLine("Completed!");
if (e.Error != null)
logBox.WriteTimedLogLine(e.Error.ToString());
}
It stops in the catch line This is the error message I get:
System.InvalidOperationException: Cross-thread operation not valid:
Control 'richTextBoxAll' accessed from a thread other than the thread
it was created on.
I am pretty new user for BackgroundWorker and may not really be aware how all these can happen. I am hoping that some more experienced can tell what is wrong with my code. I am looking forward for your guidance.
This should be a classic case of Thread Affinity. Since the BackgroundWorker runs on a different thread other than the UI thread you need to call .Invoke. Checkout this link here to see an example of an extension method that encapsulates thread-safe invocation.
With this extension method you can write WinForms thread-safe code like this:
this.ThreadSafeInvoke(() => logBox.WriteTimedLogLine("loop " + (i + 1).ToString()));
The key here is that since your execution on a different thread, the .InvokeRequired bool will return true and then you'll execute the Action passed into the ThreadSafeInvoke via the .Invoke -- which will marshal back to the UI thread.
If you do not want to have an extension method simply do the following instead:
this.Invoke(new MethodInvoker(() =>
logBox.WriteTimedLogLine("loop " + (i + 1).ToString())));
The advantages to the extension method are obvious. I hope you find this helpful.
I write code, which call thread with parameters. But my program is windows forms. So how change code, that wait thread, and that GUI of my program not freeze?
var t=new Thread(()=>SomeMethod(SomeParameter));
t.Start();
//t.Wait?
If you don't have await available, the simplest solution is to use a BackgroundWorker:
var bw = new BackgroundWorker();
bw.DoWork += (sender, args) => {
// do your lengthy stuff here -- this will happen in a separate thread
...
}
bw.RunWorkerCompleted += (sender, args) => {
if (args.Error != null) // if an exception occurred during DoWork,
MessageBox.Show(args.Error.ToString()); // do your error handling here
// Put here the stuff you wanted to put after `t.Wait`
...
}
bw.RunWorkerAsync(); // start the background worker
RunWorkerCompleted runs in the UI thread, so you will be able to update your UI there (as opposed to stuff happening in DoWork).
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
I have a BackgroundWorker to call a function to do a long process at BackgroundWorker _DoWork, when error occur in the function I will prompt a customized messagebox:
WPFMessageBoxResult result = WPFMessageBox.Show("Activation Fail", "Error!!", WPFMessageBoxButtons.OK, WPFMessageBoxImage.Error);
The exception below happens at WPFMessageBoxResult class :
The calling thread must be STA, because many UI components require this.
Thank you.
You should not try to interact with any UI components from a background thread.
One way could be to catch the exception in your doWork method and assign it to the backgroundworker's result property and then check if that result is a type of exception or not null if you are not using the result for anything else. then check for it in the backgroundWorker_completed event.
BackgroundWorker_DoWork(sender, )
{
try
{
// do work
}
catch (Exception ex)
{
BackgroundWorker w = sender as BackgroundWorker;
if (w != null)
w.Result = ex;
}
}
then
BackgroundWorker_Completed()
{
if (s.Result != null && e.Result is Exception)
{
Exception ex = e.Result as Exception;
// do something with ex
}
}
Usually with Winforms/WPF you use Invoke() to jump onto the UI thread if you need to interact with the UI from a long-running task. You can call invoke from any object that belongs to the UI, but be sure when in the invoke scope to only do as little code as possible. Since this code is on the UI thread it will block/hang the UI if it takes too long.
public void BackgroundWorkerMethod()
{
try
{
// do work
}
catch (Exception e)
{
uiObject.Invoke(new Action(() => {
// now you are on the UI thread
Message.ShowMessage(e.Message);
});
}
}
Your background thread is just a worker thread and not a user interface thread. WPF and WinForms both require that the thread performing user interface actions be marked as STA (Single Threaded Apartment) because the user interface code is not thread safe. They also require that you add a message pump so that windows messages are dispatched.
I recommend that instead of showing the message box in your worker thread you send a message to your main user interface thread and have that thread show the message box. To do this you should pass a reference to the Dispatcher from the main UI thread into the worker thread. Then use the Dispatcher.BeginInvoke to request a delegate be called back on the main thread.
Alternatively you can wait for the background thread to complete and then check the result and show the appropriate answer to the user. Either way the worker thread should not be directly interacting with the user interface.
You must use this method
void BGW_DoWork(object sender, DoWorkEventArgs e)
{
try
{
Dispatcher.BeginInvoke(new Action(() =>
{
Button btn = new Button();
btn.Width = 100;
btn.Height = 50;
btn.Content = "Test";
myG.Children.Add(btn);
}
));
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
I've got a threaded invoke call that never returns.
The thread runs just fine right up until I call the line that ways, "owner.Invoke(methInvoker);"
When debugging, I can slowly step, step, step, but once I hit owner.Invoke... it's Over!
Control owner;
public event ReportCeProgressDelegate ProgressChanged;
public void ReportProgress(int step, object data) {
if ((owner != null) && (ProgressChanged != null)) {
if (!CancellationPending) {
ThreadEventArg e = new ThreadEventArg(step, data);
if (owner.InvokeRequired) {
MethodInvoker methInvoker = delegate { ProgressChanged(this, e); };
owner.Invoke(methInvoker);
} else {
ProgressChanged(this, e);
}
} else {
mreReporter.Set();
mreReporter.Close();
}
}
}
FYI: This is a custom class that mimics the BackgroundWorker class, which is not available on controls that do not have Forms.
Thinking Invoke might not be required, I manually stepped the cursor in the debugger over that part of the code and tried calling ProgressChanged directly, but VS2010's debugger threw a cross thread exception.
EDIT:
Due to the first 3 comments I have received, I wanted to update with my ProgressChanged method:
worker.ProgressChanged += delegate(object sender, ThreadEventArg e) {
if (progressBar1.Style != ProgressBarStyle.Continuous) {
progressBar1.Value = 0;
object data = e.Data;
if (data != null) {
progressBar1.Maximum = 100;
}
progressBar1.Style = ProgressBarStyle.Continuous;
}
progressBar1.Value = e.ProgressPercentage;
};
There is a breakpoint on the first line of the anonymous method, but it never gets hit either.
EDIT 2
Here is a more complete listing of the call to the thread:
List<TableData> tList = CollectTablesFromForm();
if (0 < tList.Count) {
using (SqlCeReporter worker = new SqlCeReporter(this)) {
for (int i = 0; i < tList.Count; i++) {
ManualResetEvent mre = new ManualResetEvent(false);
worker.StartThread += SqlCeClass.SaveSqlCeDataTable;
worker.ProgressChanged += delegate(object sender, ThreadEventArg e) {
if (progressBar1.Style != ProgressBarStyle.Continuous) {
progressBar1.Value = 0;
object data = e.Data;
if (data != null) {
progressBar1.Maximum = 100;
}
progressBar1.Style = ProgressBarStyle.Continuous;
}
progressBar1.Value = e.ProgressPercentage;
};
worker.ThreadCompleted += delegate(object sender, ThreadResultArg e) {
Cursor = Cursors.Default;
progressBar1.Visible = false;
progressBar1.Style = ProgressBarStyle.Blocks;
if (e.Error == null) {
if (e.Cancelled) {
MessageBox.Show(this, "Save Action was Cancelled.", "Save Table " + tList[i].TableName);
}
} else {
MessageBox.Show(this, e.Error.Message, "Error Saving Table " + tList[i].TableName, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
mre.Set();
};
worker.RunWorkerAsync(tList[i]);
progressBar1.Value = 0;
progressBar1.Style = ProgressBarStyle.Marquee;
progressBar1.Visible = true;
Cursor = Cursors.WaitCursor;
mre.WaitOne();
}
}
}
I hope this isn't overkill! I hate presenting too much information, because then I get people critiquing my style. :)
worker.RunWorkerAsync(tList[i]);
//...
mre.WaitOne();
That's a guaranteed deadlock. The delegate you pass to Control.Begin/Invoke() can only run when the UI thread is idle, having re-entered the message loop. Your UI thread isn't idle, it is blocked on the WaitOne() call. That call can't complete until your worker thread completes. Your worker thread can't complete until the Invoke() call is completed. That call can't complete until the UI thread goes idle. Deadlock city.
Blocking the UI thread is fundamentally a wrong thing to do. Not just because of .NET plumbing, COM already requires it to never block. That's why BGW has a RunWorkerCompleted event.
It is likely that you have deadlocked the UI and worker threads. Control.Invoke marshals the execution of a delegate onto the UI thread by posting a message to the UI thread's message queue and then waits for that message to be processed which in turn means the execution of the delegate has to complete before Control.Invoke returns. But, what if your UI thread is busy doing something else beside dispatching and processing messages? I can see from your code that a ManualResetEvent may be in play here. Is your UI thread blocked on a call to WaitOne by chance? If so that could definitely be the problem. Since WaitOne does not pump messages it will block the UI thread which will lead to a deadlock when Control.Invoke is called from your worker thread.
If you want your ProgressChanged event to behave like it does with BackgroundWorker then you will need to call Control.Invoke to get those event handlers onto the UI thread. That is the way BackgroundWorker works anyway. Of course, you do not have to mimic the BackgroundWorker class exactly in that respect as long as you are prepared to have the callers do their own marshaling when handling the ProgressChanged event.