我做了一个全天候运行的应用程序,三个Backgroundworkers
以不同的间隔运行。
在他们的DoWork
中,我做了一些Dispatcher.BeginInvoke
,所以它更新了一些图表。问题是它在夜间崩溃,我不确定为什么。我已经将Dispatcher.BeginInvoke
包装在 try/catch 中,但由于我正在调用 UI 线程,我想我可能会在Dispatcher.BeginInvoke
内执行 try catch。
有关系吗?
测试在RunWorkerDone 方法中的 EventArgs Error 属性是否不为 null
backgroundWorker.RunWorkerCompleted += (s, e) =>
{
if (e.Error != null)
{
// handle error
}
}
try/catch
将仅适用于其当前线程中发生的事情。如果在另一个线程中发生错误,工作线程将完成,但你不知道原因,除非您检查Error
属性。
与Dispatcher.BeginInvoke
一起排队的回调是异步的。您应该观察传入Dispatcher.BeginInvoke
委托的所有异常,因为它们不会传播到委托之外的任何地方(除了作为Application.Current.DispatcherUnhandledException
、AppDomain.CurrentDomain.UnhandledException
事件和作为DispatcherOperation.Task.Exception
属性,见下文)。如果它们不处理,它们将使 UI 线程上的核心Dispatcher
事件循环中的应用崩溃。
这也包括RunWorkerCompletedEventArgs.Error
。在委托Dispatcher.BeginInvoke
抛出的异常将不作为Error
RunWorkerCompletedEvent
事件时可用。
下面是一个说明问题的简单示例。请注意e.Error
是如何null
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();
要解决此问题,请观察如下所示的异常:
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());
}
}));
或者,您可以跟踪Dispatcher.BeginInvoke
返回的所有DispatcherOperation
:
var invokes = new List<DispatcherOperation>();
// do the work on a background thread
// ..
invokes.Add(Dispatcher.BeginInvoke(new Action(() =>
{ /* update the UI */ }))));
然后,您可以检查排队的每个调用DispatcherOperation.Task.Exception
Dispatcher.BeginInvoke
。不过,我认为这是不可行的,除非您可以阻止invokes
列表无休止地增长。