如何等待BackgroundWorker取消?
考虑一个假设的方法来为你做一些事情:
public class DoesStuff
{
BackgroundWorker _worker = new BackgroundWorker();
...
public void CancelDoingStuff()
{
_worker.CancelAsync();
//todo: Figure out a way to wait for BackgroundWorker to be cancelled.
}
}
如何等待BackgroundWorker完成?
过去人们已经尝试过:
while (_worker.IsBusy)
{
Sleep(100);
}
但是这种死锁,因为在处理RunWorkerCompleted
事件之前IsBusy
不会被清除,并且在应用程序空闲之前无法处理该事件。 在工作完成之前,应用程序不会闲置。 (另外,这是一个繁忙的循环 - 令人厌恶。)
其他人则增加了建议:
while (_worker.IsBusy)
{
Application.DoEvents();
}
问题在于Application.DoEvents()
会导致当前队列中的消息被处理,从而导致重入问题(.NET不可重入)。
我希望使用一些涉及事件同步对象的解决方案,其中代码等待事件 - 工作人员的RunWorkerCompleted
事件处理程序设置。 就像是:
Event _workerDoneEvent = new WaitHandle();
public void CancelDoingStuff()
{
_worker.CancelAsync();
_workerDoneEvent.WaitOne();
}
private void RunWorkerCompletedEventHandler(sender object, RunWorkerCompletedEventArgs e)
{
_workerDoneEvent.SetEvent();
}
但是我回到了僵局:在应用程序闲置之前,事件处理程序无法运行,并且应用程序不会闲置,因为它正在等待事件。
那么如何等待BackgroundWorker完成?
更新人们似乎对这个问题感到困惑。 他们似乎认为我将使用BackgroundWorker作为:
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += MyWork;
worker.RunWorkerAsync();
WaitForWorkerToFinish(worker);
那不是那个,那不是我正在做的,这不是在这里被问到的。 如果是这样的话,那么使用后台工作者就没有意义。
如果我理解你的要求是正确的,你可以做这样的事情(代码没有经过测试,但显示了总体思路):
private BackgroundWorker worker = new BackgroundWorker();
private AutoResetEvent _resetEvent = new AutoResetEvent(false);
public Form1()
{
InitializeComponent();
worker.DoWork += worker_DoWork;
}
public void Cancel()
{
worker.CancelAsync();
_resetEvent.WaitOne(); // will block until _resetEvent.Set() call made
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
while(!e.Cancel)
{
// do something
}
_resetEvent.Set(); // signal that worker is done
}
这种回应存在问题。 UI在等待时需要继续处理消息,否则不会重新绘制,如果您的后台工作人员花费很长时间来响应取消请求,这将会成为问题。
第二个缺陷是,如果工作线程抛出异常,将永远不会调用_resetEvent.Set()
- 让主线程无限期地等待 - 但是这个缺陷很容易通过try / finally块修复。
一种方法是显示一个模式对话框,该对话框有一个定时器,用于重复检查后台工作人员是否已完成工作(或者在您的案例中完成取消)。 一旦后台工作完成后,模式对话框会将控制权返回给您的应用程序。 直到发生这种情况,用户才能与UI进行交互。
另一种方法(假设您最多打开一个无模式窗口)是设置ActiveForm.Enabled = false,然后循环Application,DoEvents直到后台工作者完成取消,之后您可以再次设置ActiveForm.Enabled = true。
几乎所有人都对这个问题感到困惑,并不了解工人的使用方式。
考虑一个RunWorkerComplete事件处理程序:
private void OnRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (!e.Cancelled)
{
rocketOnPad = false;
label1.Text = "Rocket launch complete.";
}
else
{
rocketOnPad = true;
label1.Text = "Rocket launch aborted.";
}
worker = null;
}
一切都很好。
现在来了一个情况,呼叫者需要中止倒计时,因为他们需要执行火箭的紧急自毁。
private void BlowUpRocket()
{
if (worker != null)
{
worker.CancelAsync();
WaitForWorkerToFinish(worker);
worker = null;
}
StartClaxon();
SelfDestruct();
}
还有一种情况是我们需要打开火箭的进入门,但不是在倒计时时进行:
private void OpenAccessGates()
{
if (worker != null)
{
worker.CancelAsync();
WaitForWorkerToFinish(worker);
worker = null;
}
if (!rocketOnPad)
DisengageAllGateLatches();
}
最后,我们需要对火箭进行去燃料,但在倒计时期间不允许这样做:
private void DrainRocket()
{
if (worker != null)
{
worker.CancelAsync();
WaitForWorkerToFinish(worker);
worker = null;
}
if (rocketOnPad)
OpenFuelValves();
}
如果没有等待工作人员取消的能力,我们必须将所有三种方法移至RunWorkerCompletedEvent:
private void OnRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (!e.Cancelled)
{
rocketOnPad = false;
label1.Text = "Rocket launch complete.";
}
else
{
rocketOnPad = true;
label1.Text = "Rocket launch aborted.";
}
worker = null;
if (delayedBlowUpRocket)
BlowUpRocket();
else if (delayedOpenAccessGates)
OpenAccessGates();
else if (delayedDrainRocket)
DrainRocket();
}
private void BlowUpRocket()
{
if (worker != null)
{
delayedBlowUpRocket = true;
worker.CancelAsync();
return;
}
StartClaxon();
SelfDestruct();
}
private void OpenAccessGates()
{
if (worker != null)
{
delayedOpenAccessGates = true;
worker.CancelAsync();
return;
}
if (!rocketOnPad)
DisengageAllGateLatches();
}
private void DrainRocket()
{
if (worker != null)
{
delayedDrainRocket = true;
worker.CancelAsync();
return;
}
if (rocketOnPad)
OpenFuelValves();
}
现在我可以写我的代码,但我只是不会。 我不在乎,我只是不在乎。
链接地址: http://www.djcxy.com/p/48273.html上一篇: How to wait for a BackgroundWorker to cancel?
下一篇: Find if process is responding without using System.Diagnostics.Process.Responding