0

次の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) からメソッドを呼び出します。そのメソッドには、プロセス作業を行うために複数のスレッドを呼び出すパターンがあります。これらのスレッドの 1 つがエラーをスローすると、アプリケーションがクラッシュし、イベント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;
}

各スレッドのコールバックを使用して Web ページを取得し、そのページを処理します。

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 スレッドにディスパッチします。DispatcherUnhandledException が発生するように、それらを処理せずにメイン UI スレッドで再スローします。

私の質問は 1) 例外が捕捉されないのはなぜですか? 使用する必要がApplication.DispatcherUnhandledExceptionありますか?もしそうなら、どうすればこれを達成できますか? 最終的には、これらの例外を にスローしたいと思いBackgroundWorkerます。提案やコメントをいただければ幸いです。

アップデート

私は await/async と Tasks で TPL を使用することに取り組んでおり、コードを更新しました。これは、例外を に戻しているので、ある程度成功してい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イベントから直接例外をスローしているため、これがどのようになるかわかりません。誰かがこの動作を説明できますか?

4

1 に答える 1

0

Backgroundworker は非同期メソッドとしてセットアップされます。これにより、イベントが完了して例外がRunWorkerCompleted発生する前にイベントが発生していると思います。DoWorkDoWork メソッドを更新して async コンパイラ キーワードを削除し、await を削除すると、RunWorkerCompletedイベントで指定されたメソッドに例外が伝播され、e.Error != null

于 2013-09-25T18:23:31.520 に答える