如何等待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