0

そのため、backgroundworkerを使用して各ページを処理するWebページスクレーパーがあります。また、MVVMライトフレームワークを使用していることにも言及したいと思います。

MainViewModelコンストラクター内で、backgroundworkerを初期化しています。

backgroundWorker = new BackgroundWorker()
        {
            WorkerReportsProgress = true,
            WorkerSupportsCancellation = true
        };
        backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);

WebBrowserコントロールのLoadCompletedイベントで、backgroundworkerを起動します。

wb = sender; //sender is the webbrowser control
        if (!backgroundWorker.IsBusy)
        {
            backgroundWorker.RunWorkerAsync();
        }

次の2つの方法は、DoWorkとStopWorkです。

private System.Threading.AutoResetEvent _resetEvent = new System.Threading.AutoResetEvent(false);
    private object wb;

    void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker wk = sender as BackgroundWorker;

        if (wb != null)
        {
            FetchPage(wb);

            if (wk.CancellationPending)
            {
                MessageBox.Show("Cancellation pending!");
            }

            _resetEvent.Set();
        }
    }

    private void StopWork(object sender)
    {
        backgroundWorker.CancelAsync();
        _resetEvent.WaitOne();
    }

fetchpageメソッドは、webbrowserコントロールのソースコードを取得し、コンテンツの解析を開始します。

FetchPageの内部では、BeginInvokeを使用してUIスレッドを更新しています。

Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(
            () =>
            { ... }

私の問題:[キャンセル]ボタンを押すと、StopWorkメソッドが呼び出され、backgroundWorkerのcancelプロパティが正しくtrueに設定されますが、アプリは続行されます。私のif(wk.CancellationPending)は常にfalseです。

私がここで何を間違っているのかについて何か考えはありますか?私はオンラインとここStackOverflowでたくさんの例を見ました、そしてそれらはすべて私がすでにしたのと同じことを述べています。

ありがとう。

編集:

Ernosが応答した後、CancellationPendingプロパティをFetchPageメソッドに渡して、別の場所で確認しようとしましたが、処理が停止しませんでした。

void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker wk = sender as BackgroundWorker;

        if (wb != null)
        {
            FetchPage(wb, wk.CancellationPending);

            _resetEvent.Set();
        }
    }

FetchPageの内部では、BeginInvokeを使用してUIスレッドを更新しています。

private void FetchPage(object sender, bool stopAll)
{
     if (stopAll)
     {
         return;
     }
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(
            () =>
            { ... }

私が試し、取り組んだことは次のとおりです。

private bool stopAllWork = false;

...

private void StopWork(object sender)
    {
        stopAllWork = true;
        backgroundWorker.CancelAsync();
        _resetEvent.WaitOne();
    }

そしてDoWorkの内部:

void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker wk = sender as BackgroundWorker;

        if (wb != null)
        {
            FetchPage(wb, stopAllWork);

            _resetEvent.Set();
        }
    }

さて、この実装のために、私の懸念は、不正なbackgroundWorkersが残っているかどうかです。

4

2 に答える 2

2

メソッドのCancellationPending内部を評価する必要があります。FetchPage

あなたは仕事の負荷の後にそれをチェックしています。

于 2012-08-21T19:50:03.710 に答える
1

エルノ そうですね。すべての作業を完了した後、キャンセルされたかどうかを確認しています。モジュール化を維持するには、バックグラウンド ワーカーを FetchPage に渡さないことを検討してください。キャンセルする必要がある場合に返す関数を渡します。

public void FetchPage(WebBrowser wb, Func<bool> cancelNow)
{
  ...
  if(cancelNow()) {
    return;
  }
  ...
}

あなたはそれをそのように呼ぶでしょう

FetchPage(wb, () => wk.CancellationPending);

ただし、バックグラウンド ワーカーを使用しない別のアプリケーションにその関数を配置して、次のように呼び出すこともできます。

FetchPage(wb, () => false);

注: 作業が完了しているため、キャンセルする必要があるかどうかを確認してください。たとえば、ほとんどの作業がループで発生する場合は、ループ内を確認します。一連のステップがある場合は、各ステップ間を確認してください。

于 2012-08-21T20:00:42.187 に答える