后台工作者不取消

时间:2012-08-21 19:47:34

标签: c# wpf .net-4.0 backgroundworker

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

在我的MainViewModel构造函数中我正在初始化backgroundworker:

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上的cancel属性被正确设置为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();
        }
    }

现在,由于这个实现,我担心的是,是否还会有任何流氓背景工作者?

2 个答案:

答案 0 :(得分:2)

您需要评估CancellationPending方法中的FetchPage

你正在检查工作量。

答案 1 :(得分:1)

Erno是对的。在完成所有工作后,您正在检查它是否被取消。为了保持模块化,您可以考虑不将后台工作程序传递给FetchPage;相反,如果您要取消,则传递一个返回的函数。

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

你会这样称呼它

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

但您可以将该函数放在另一个不使用后台工作程序的应用程序中,并像这样调用它:

FetchPage(wb, () => false);

注意:请确保在工作完成时检查是否应取消。例如,如果大多数工作发生在循环中,请检查循环内部。如果有一系列步骤,请检查每个步骤。