BackgroundWorker 启动其他有异常的线程.未捕获异常

本文关键字:线程 捕获异常 异常 启动 其他 BackgroundWorker | 更新日期: 2023-09-27 18:31:09

我有一个从我的主 UI 线程调用的BackgroundWorker,如下所示。

在主窗口上,我声明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 事件的帮助页面,它指出:

您需要编写代码来执行以下操作: 在 上处理异常 后台线程。将这些异常调度到主 UI 线。在主 UI 线程上重新抛出它们,而不将它们处理到 允许引发调度程序未处理异常。

我的问题是1)为什么没有抓住这个人? 我应该使用Application.DispatcherUnhandledException,如果是,我该如何完成此操作? 我最终想把这些例外抛给BackgroundWorker. 任何建议或意见将不胜感激。

更新

我一直在努力将 TPL 与 await/async 和任务一起使用,并更新了我的代码。 这有点成功,因为我现在将异常恢复到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事件中抛出异常。 有人可以解释这种行为吗?

BackgroundWorker 启动其他有异常的线程.未捕获异常

后台工作者设置为异步方法。我相信这会导致RunWorkerCompleted事件在DoWork事件完成并引发异常之前触发。 通过更新 DoWork 方法以删除异步编译器关键字并删除任何等待,异常将传播回 RunWorkerCompleted 事件指定的方法并e.Error != null