我在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
答案 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方法检查此属性的值。