当前位置:  开发笔记 > 编程语言 > 正文

如何等待BackgroundWorker取消?

如何解决《如何等待BackgroundWorker取消?》经验,为你挑选了3个好方法。

考虑一个对象的假设方法,为您做一些事情:

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);
}

但是这种死锁,因为IsBusyRunWorkerCompleted事件处理之后才会被清除,并且在应用程序空闲之前无法处理该事件.在工人完成之前,应用程序不会闲置.(另外,这是一个繁忙的循环 - 恶心.)

其他人已添加建议将其融入:

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);

不是它,这不是我正在做的事情,这不是在这里被问到的.如果是这种情况,使用后台工作者就没有意义了.



1> Fredrik Kals..:

如果我理解你的要求是正确的,你可以做这样的事情(代码没有经过测试,但显示了一般的想法):

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(例如,没有重新绘制).最好使用以下方法之一来停止与UI的交互:http://stackoverflow.com/questions/123661/net-how-to-wait-for-a-backgroundworker-to-complete#126570
我需要检查`((BackgroundWorker)发送者).CancellationPending`来获取取消事件

2> Joe..:

此响应存在问题.UI需要在您等待时继续处理消息,否则它将不会重新绘制,如果您的后台工作程序需要很长时间来响应取消请求,这将是一个问题.

第二个缺陷是,_resetEvent.Set()如果工作线程抛出异常,将永远不会被调用 - 让主线程无限期地等待 - 但是这个缺陷可以通过try/finally块轻松修复.

一种方法是显示一个模态对话框,其中有一个计时器,可以反复检查后台工作程序是否已完成工作(或在您的情况下完成取消).后台工作程序完成后,模态对话框会将控制权返回给您的应用程序.在发生这种情况之前,用户无法与UI进行交互.

另一种方法(假设您最多打开一个无模式窗口)是设置ActiveForm.Enabled = false,然后在Application,DoEvents上循环,直到后台工作程序完成取消,之后您可以再次设置ActiveForm.Enabled = true.


这可能是一个问题,但它从根本上被接受为问题的一部分,"如何**等待**以取消BackgroundWorker".等待意味着你等待,你什么都不做.这还包括处理消息.如果我不想等待后台工作人员,你可以调用.CancelAsync.但这不是设计要求.

3> Ian Boyd..:

几乎所有人都对这个问题感到困惑,并且不了解工人的使用方式.

考虑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();
}

如果没有等待worker取消的能力,我们必须将所有三个方法移动到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();
}

现在我可以像这样写我的代码,但我不会.我不在乎,我不是.


WaitForWorkerToFinish方法在哪里?任何完整的源代码?
推荐阅读
mobiledu2402852357
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有