我正在使用BackgroundWorker执行长计算(一次只有一次这样的计算)。
用户可以取消该工作人员(调用worker.CancelAsync)。
在worker.DoWork方法中,我定期检查取消挂起标志,然后从方法返回。
然后从工作人员处获得Completed事件,我可以检查该工作人员是否已被取消。此外,这是重要的事情,当我检测到取消时,我会做一些额外的清理工作。
我确信如果用户取消了工作者并且它已经从DoWork方法返回,则可能会出现问题。在那种情况下,我真的想知道工人被取消所以我可以清理......
有没有更好的方法来处理工作人员的清理取消程序?
答案 0 :(得分:2)
您的DoWork
事件处理程序应定期检查BackgroundWorker.CancellationPending
,并在返回前将DoWorkEventArgs.Cancel
设置为true。
您的RunWorkerCompleted
事件处理程序应检查RunWorkerCompletedEventArgs.Cancelled
属性以确定DoWork
事件处理程序是否已取消(将DoWorkEventArgs.Cancel
设置为true)。
如果出现竞争条件,可能会发生用户请求取消(BackgroundWorker.CancellationPending
为真)但工作人员没有看到它(RunWorkerCompletedEventArgs.Cancelled
为假)。您可以测试这两个属性以确定是否已发生这种情况,并执行您选择的任何操作(将其视为成功完成 - 因为工作人员确实已成功完成,或作为取消 - 因为用户已取消并且不关心任何更多)。
我没有看到发生什么事情有任何含糊之处的情况。
修改强>
在回复评论时 - 如果有几个类需要检测CancellationPending
,那么实际上没有其他方法可以向这些类传递对BackgroundWorker
等允许它们的类型的引用检索此信息。您可以将其抽象为一个界面,这是我通常在回答有关BackgroundWorkers的问题时所描述的。但是您仍然需要将对该接口实现的类型的引用传递给您的工作类。
如果您希望您的工作类能够设置DoWorkEventArgs.Cancel
,您需要传递对此的引用,或者采用允许您的工作者的不同约定(例如布尔返回值或自定义异常)表示已取消取消的类。
答案 1 :(得分:2)
我不认为这里的其他答案实际上解决了所有情况下可能的竞争条件。问题是,无论DoWork是否真的有机会检查标志,在DoWork结束后,CancellationPending似乎都会被设置为false。如果你愿意,你可以自己测试一下。为了解决这个问题,我必须创建一个子类,如下所示:
Public Class CancelTrackingBackgroundWorker
Inherits System.ComponentModel.BackgroundWorker
Public CancelRequested As Boolean = False
Public Sub TrackableCancelAsync()
Me.CancelRequested = True
Me.CancelAsync()
End Sub
End Class
(顺便说一句,令人讨厌的CancelAsync是不可覆盖的。)然后我调用TrackableCancelAsync而不是旧的CancelAsync,我在我的代码中检查这个新的CancelRequested标志。只要你只从UI线程调用和检查(应该是标准的),那就应该处理你的线程问题。
答案 2 :(得分:1)
保证在RunWorkerCompleted事件处理程序中没有竞争,因为它在UI线程上运行。这应该始终有效:
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
var bgw = sender as BackgroundWorker;
if (e.Cancelled || bgw.CancellationPending) {
// etc..
}
}
答案 3 :(得分:0)
有没有理由不能在异步运行的方法结束时进行清理?
我通常使用像这样的结构
private void MyThreadMethod()
{
try
{
// Thread code
}
finally
{
// Cleanup
}
}
我只使用complete事件来执行更新UI等任务,而不是在线程后进行清理。
答案 4 :(得分:0)
在没有看到您的代码的情况下,提出建议有点困难,但您可以做的一件事是在用户点击它时以及退出DoWork
方法时禁用“取消”按钮。
如果用户也可以通过按键取消,那么您还需要禁用它。
另外 - 如果DoWork
方法已经完成,那么用户没有“取消” - 或者我错过了什么。在这种情况下,即使你没有禁用取消按钮,也不需要做任何额外的事情。
为什么在工作人员完成工作后需要查看取消标志?你还想要回滚曾经改变过的东西吗?