尝试捕获 UI 线程的调用



我做了一个全天候运行的应用程序,三个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.DispatcherUnhandledExceptionAppDomain.CurrentDomain.UnhandledException事件和作为DispatcherOperation.Task.Exception属性,见下文)。如果它们不处理,它们将使 UI 线程上的核心Dispatcher事件循环中的应用崩溃。

这也包括RunWorkerCompletedEventArgs.Error。在委托Dispatcher.BeginInvoke抛出的异常将不作为ErrorRunWorkerCompletedEvent事件时可用。

下面是一个说明问题的简单示例。请注意e.Error是如何nullRunWorkerCompleted

// 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.ExceptionDispatcher.BeginInvoke。不过,我认为这是不可行的,除非您可以阻止invokes列表无休止地增长。

最新更新