0

所以我有一个网页抓取工具,它使用 backgroundworker 来处理每个页面。我还想提一下,我正在使用 MVVM 轻框架。

在我的 MainViewModel 构造函数中,我正在初始化后台工作程序:

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

在 WebBrowser 控件的 LoadCompleted 事件上,我启动了后台工作程序:

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

我接下来的两种方法是 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 上的取消属性被正确设置为 true,但应用程序仍在继续。我的 if (wk.CancellationPending) 总是假的。

知道我在这里做错了什么吗?我在网上和 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 回答