我有一个BackgroundWorker
我从我的主UI线程调用如下。
在MainWindow上,我声明了BackgroundWorker
private BackgroundWorker backgroundWorkerRefreshFromWeb = new BackgroundWorker();
在构造函数中,我将其设置如下。
backgroundWorkerRefreshFromWeb.WorkerReportsProgress = true;
backgroundWorkerRefreshFromWeb.WorkerSupportsCancellation = true;
backgroundWorkerRefreshFromWeb.DoWork +=
new DoWorkEventHandler(backgroundWorkerRefreshFromWeb_DoWork);
backgroundWorkerRefreshFromWeb.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(backgroundWorkerRefreshFromWeb_RunWorkerCompleted);
backgroundWorkerRefreshFromWeb.ProgressChanged +=
new ProgressChangedEventHandler(backgroundWorker_ProgressChanged);
在构造函数的最后,我调用以下方法来启动它。
private void RefreshWebDataTimer(Object state)
{
if (!backgroundWorkerRefreshFromWeb.IsBusy && !backgroundWorkerLoadFromDB.IsBusy)
{
System.Diagnostics.Debug.Print("Refresh Timer Started at {0}", DateTime.Now);
backgroundWorkerRefreshFromWeb.RunWorkerAsync(nfl);
}
}
DoWork
事件处理程序从另一个项目(DLL)调用方法。该方法有一个模式,它调用多个线程来进行处理工作。当其中一个线程抛出错误时,应用程序崩溃,BackgroundWorker
未在RunWorkerCompleted
事件中捕获它。这种模式很复杂(可能过于复杂),但如下所示。
在DoWork事件处理程序调用的方法中,我在包装器中创建了一组“子工作者”线程,如下所示...然后等待所有线程完成处理,然后再继续。
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;
}
我使用每个线程的回调来获取一个网页,然后处理该页面:
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();
}
然后我在Html.ParseRoster中抛出异常,但它没有被抓住。这与BackgroundWorker
不同。我不知道为什么BackgroundWorker
没有抓住它。由于我在等待所有线程完成之前我不认为RunWorkerCompleted
事件会在我完成之前运行。
我查看了Application.DispatcherUnhandledException event的帮助页面,并指出:
您需要编写代码来执行以下操作:处理例外 后台线程。将这些例外发送到主UI 线。将它们重新放在主UI线程上而不处理它们 允许引发DispatcherUnhandledException。
我的问题是1)为什么没有抓住这个重复?我应该使用Application.DispatcherUnhandledException
,如果是,我该如何实现?我最终想把这些例外抛给BackgroundWorker
。任何建议或意见将不胜感激。
更新
我一直在使用TPL和await / async以及Tasks并更新我的代码。这有点成功,因为我现在将异常带回BackgroundWorker
。忽略我现在如何将异常返回到DoWork
事件...我通过添加try / catch块来检查我是否正在获取异常并且正在捕获并重新抛出异常。这是我的DoWork
事件
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;
}
}
在调试器中,我得到一个异常并看到它被抛出。但是,当它转到RunWorkerCompleted
事件时,RunWorkerCompletedEventArgs e
会显示e.Error == null
。我不明白这是怎么回事,因为我直接从DoWork
事件中抛出异常。有人可以解释这种行为吗?
答案 0 :(得分:0)
将Backgroundworker设置为异步方法。我相信这会导致RunWorkerCompleted
事件在DoWork
事件完成并且引发异常之前触发。通过更新DoWork方法以删除异步编译器关键字并删除任何等待,异常将传播回RunWorkerCompleted
事件和e.Error != null