使用Dispatcher.BeginInvoke 排队的回调是异步的。您应该观察您传递给Dispatcher.BeginInvoke的委托中的所有异常,因为它们不会在它之外的任何地方传播(除了Application.Current.DispatcherUnhandledException、AppDomain.CurrentDomain.UnhandledException 事件和DispatcherOperation.Task.Exception 属性,请参阅以下)。如果他们放任不管,他们将在 UI 线程上的核心 Dispatcher 事件循环内使应用程序崩溃。
这也包括RunWorkerCompletedEventArgs.Error。在RunWorkerCompletedEvent 事件发生时,Dispatcher.BeginInvoke 委托中抛出的异常将在该处不可用为Error。
这里有一个简单的例子来说明这个问题。注意e.Error 在RunWorkerCompleted 中是null:
// 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 列表无休止地增长。