BackgroundWorker取消

时间:2014-11-24 04:40:31

标签: c# winforms backgroundworker cancellation

我在winforms应用程序中使用BackgroundWorker来执行在另一个类中执行的长时间运行的任务(执行数据库操作)。由于所有工作都在另一个班级完成,因此取消并不那么简单。我在另一个类(GenerateStats)中使用一个事件来在后台操作完成时更新进度。我想取消操作做类似的事情。我不能在DoWork函数中调用cancellationPending,因为该方法在完成之前永远不会看到它并且会失败。我希望取消功能,而无需将BackgroundWorker传递给generateForSubject()。无论如何,这可以支持generateForSubject()GenerateStats方法的取消。这是操作执行的类的实例化:

GenerateStats genStats = new GenerateStats();

这是我的DoWork函数,只要调用其他类中的事件,就会调用ReportProgress方法。它还调用执行操作的其他类generateForSubject()中的方法。

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

    genStats.ProgressChanged += (s, pe) => worker.ReportProgress(pe.ProgressPercentage, pe.UserState);
    genStats.generateForSubject();
}

这是按钮点击事件处理程序,应启动取消并运行CancelAsync()

private void btnStop_Click(object sender, EventArgs e)
{
    if (backgroundWorker.IsBusy)
    {
        backgroundWorker.CancelAsync();
    }
}

这是我执行操作的单独类,同时创建了ProgressChanged事件处理程序,因此我的类可以使用有关进度的信息更新表单。如果取消可以同样执行,那将是非常棒的。

public event ProgressChangedEventHandler ProgressChanged;

protected virtual void OnProgressChanged(int progress, string message)
{
    if (ProgressChanged != null)
    {
        ProgressChanged(this, new ProgressChangedEventArgs(progress, message));
    }
}

public void generateForSubject()
{
    //Perform db operation not important, but it takes time

    OnProgressChanged(33, "Finished 1st set of stats");
    //I hope to check for cancellation here

    //Perform db operation not important, but it takes time

    OnProgressChanged(66, "Finished 2nd set of stats");
    //I hope to check for cancellation here        

    //Perform db operation not important, but it takes time

    OnProgressChanged(99, "Finished 3rd set of stats");
    //I hope to check for cancellation here
}

只是为了澄清我的问题是否存在任何不确定性,是否有任何方法可以支持在其他类中取消我的backgroundWorker而不将backgroundWorker传递给该方法。如果我完全没有办法,那么我将通过backgroundWorker

2 个答案:

答案 0 :(得分:4)

如果您可以更加具体地了解您不愿意通过BackgroundWorker实例,那将会很有帮助。了解为什么这是一个设计要求可以帮助您更好地回答您的问题。

也就是说,您可以应用与ProgressChanged事件相同的理念,并委派取消检查。例如:

class GenerateStats
{
    public event EventHandler<CancelEventArgs> CheckCancel;

    private bool OnCheckCancel()
    {
        EventHandler<CancelEventArgs> handler = CheckCancel;

        if (handler != null)
        {
            CancelEventArgs e = new CancelEventArgs();

            handler(this, e);

            return e.Cancel;
        }

        return false;
    }

    public void generateForSubject()
    {
        //Perform db operation not important, but it takes time

        OnProgressChanged(33, "Finished 1st set of stats");
        if (OnCheckCancel())
        {
            // Or other cancellation logic here
            return;
        }

        //Perform db operation not important, but it takes time

        OnProgressChanged(66, "Finished 2nd set of stats");
        if (OnCheckCancel())
        {
            // Or other cancellation logic here
            return;
        }

        //Perform db operation not important, but it takes time

        OnProgressChanged(99, "Finished 3rd set of stats");
        if (OnCheckCancel())
        {
            // Or other cancellation logic here
            return;
        }
    }
}

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

    genStats.ProgressChanged += (s, pe) => worker.ReportProgress(pe.ProgressPercentage, pe.UserState);
    genStats.CheckCancel += (sender1, e1) => e1.Cancel = worker.CancellationPending;
    genStats.generateForSubject();
}

这允许GenerateStats类在没有直接访问BackgroundWorker实例的情况下检查待处理的取消,就像ProgressChanged事件允许它通过{{1}报告进度一样无法直接访问BackgroundWorker

答案 1 :(得分:0)

您应该定期检查CancellationPending属性以查看是否已取消请求。然后决定取消操作。请参阅MSDN link

来自MSDN:

  

请注意,DoWork事件处理程序中的代码可能会完成   正在进行取消请求,以及您的轮询循环   可能会错过CancellationPending设置为true。在这种情况下,   取消了System.ComponentModel.RunWorkerCompletedEventArgs中的标志   即使您的RunWorkerCompleted事件处理程序也不会设置为true   虽然取消了请求。这种情况称为a   竞争条件,是多线程编程中的常见问题。   有关多线程设计问题的更多信息,请参阅托管   线程最佳实践。

修改

如果您不希望将backgroundworker传递给generateForSubject方法。收到CancelAsync时,在类中设置属性。并在每次操作之前从generateForSubject方法检查此属性的值。