C# 取消后台任务

C# 取消后台任务,c#,winforms,backgroundworker,C#,Winforms,Backgroundworker,当我的C#应用程序关闭时,它有时会陷入清理例程。具体来说,后台工作人员没有关闭。这就是我试图关闭它的基本方式: 私有无效应用程序\u FormClosing(对象发送方,FormClosingEventArgs e) { backgroundWorker1.CancelAsync(); 虽然(backgroundWorker1.IsBusy);//被困在这里。 } 有没有其他方法可以让我这样做?我正在使用Microsoft Visual C#2008快速版。谢谢 其他信息: 后台工作程序似乎

当我的C#应用程序关闭时,它有时会陷入清理例程。具体来说,后台工作人员没有关闭。这就是我试图关闭它的基本方式:

私有无效应用程序\u FormClosing(对象发送方,FormClosingEventArgs e) { backgroundWorker1.CancelAsync(); 虽然(backgroundWorker1.IsBusy);//被困在这里。 }

有没有其他方法可以让我这样做?我正在使用Microsoft Visual C#2008快速版。谢谢

其他信息:

后台工作程序似乎没有退出。这就是我所拥有的:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
   while (!backgroundWorker1.CancellationPending)
   {
      // Do something.
   }
}
我还修改了清理代码:

private void App_FormClosing(object sender, FormClosingEventArgs e)
{
   while (backgroundWorker1.IsBusy)
   {
      backgroundWorker1.CancelAsync();
      System.Threading.Thread.Sleep(1000);
   }
}

还有什么我应该做的吗?

在后台工作线程中,您需要检查BackgroundWorker.CancellationPending标志,如果该标志为真,则退出

CancelAsync()仅设置此标志

或者换一种说法。CancelAsync()实际上并不取消任何内容。它不会中止线程或使其退出。如果工作线程处于循环中并定期检查CancellationPending标志,则它可以捕获取消请求并退出

MSDN有一个例子,尽管它在辅助例程中不使用循环

试试看:

if (this.backgroundWorker1.IsBusy) this.backgroundWorker1.CancelAsync();
说明BackgroundWorker的DoWork处理程序需要轮询CancellationPending并在请求取消时返回是正确的


也就是说,如果在应用程序关闭时发生这种情况,您也可以安全地忽略它。BackgroundWorker使用一个线程池线程,根据定义,它是一个后台线程。保持此运行不会阻止应用程序终止,并且当应用程序关闭时,线程将自动中断。

此代码保证在BGW仍在运行时死锁。在其RunWorkerCompleted事件完成运行之前,BGW无法完成。在UI线程空闲并运行消息循环之前,RunWorkerCompleted无法运行。但是UI线程不是空闲的,它卡在while循环中


如果希望BGW线程干净地完成,则必须使窗体保持活动状态。查看如何做到这一点。

一些非常好的建议,但我认为它们不能解决根本问题:取消后台任务

不幸的是,在使用
BackgroundWorker
时,任务的终止取决于任务本身。
while
循环终止的唯一方法是后台任务检查其
Cancel
属性并返回或中断当前进程

示例库

例如,考虑

private readonly BackgroundWorker worker = new BackgroundWorker ();

public void SomeFormEventForStartingBackgroundTask ()
{
    worker.DoWork += BackgroundTask_HotelCalifornia;
    worker.WorkerSupportsCancellation = true;
    worker.RunWorkerAsync ();
}

// semantically, you want to perform this task for lifetime of
// application, you may even expect that calling CancelAsync
// will out and out abort this method - that is incorrect.
// CancelAsync will only set DoWorkEventArgs.Cancel property
// to true
private void BackgroundTask_HotelCalifornia (object sender, DoWorkEventArgs e)
{
    for ( ; ;)
    {
        // because we never inspect e.Cancel, we can never leave!
    }
}

private void App_FormClosing(object sender, FormClosingEventArgs e)     
{
    // [politely] request termination
    worker.CancelAsync();

    // [politely] wait until background task terminates
    while (worker.IsBusy);
}
这是默认情况下发生的情况。现在,也许你的任务不是一个无限循环,也许它只是一个长期运行的任务。无论哪种方式,您的主线程都将阻塞[实际上它正在旋转,但不管是什么],直到任务完成,或者不阻塞(视情况而定)

如果您亲自编写并可以修改任务,那么您有一些选择

示例改进

例如,这是上述示例的更好实现

private readonly BackgroundWorker worker = new BackgroundWorker ();

// this is used to signal our main Gui thread that background
// task has completed
private readonly AutoResetEvent isWorkerStopped = 
    new AutoResentEvent (false);

public void SomeFormEventForStartingBackgroundTask ()
{
    worker.DoWork += BackgroundTask_HotelCalifornia;
    worker.RunWorkerCompleted += BackgroundTask_Completed;
    worker.WorkerSupportsCancellation = true;
    worker.RunWorkerAsync ();
}

private void BackgroundTask_HotelCalifornia (object sender, DoWorkEventArgs e)
{
    // execute until canceled
    for ( ; !e.Cancel;)
    {
        // keep in mind, this task will *block* main
        // thread until cancel flag is checked again,
        // so if you are, say crunching SETI numbers 
        // here for instance, you could still be blocking
        // a long time. but long time is better than 
        // forever ;)
    }
}

private void BackgroundTask_Completed (
    object sender, 
    RunWorkerCompletedEventArgs e)
{
    // ok, our task has stopped, set signal to 'signaled' state
    // we are complete!
    isStopped.Set ();
}

private void App_FormClosing(object sender, FormClosingEventArgs e)     
{
    // [politely] request termination
    worker.CancelAsync();

    // [politely] wait until background task terminates
    isStopped.WaitOne ();
}
虽然这是更好的,但它并没有它所能做到的那么好。如果你可以[合理地]确信你的背景任务将结束,这可能“足够好”

然而,我们[通常]想要的是这样的东西

private void App_FormClosing(object sender, FormClosingEventArgs e)     
{
    // [politely] request termination
    worker.CancelAsync();

    // [politely] wait until background task terminates
    TimeSpan gracePeriod = TimeSpan.FromMilliseconds(100);
    bool isStoppedGracefully = isStopped.WaitOne (gracePeriod);

    if (!isStoppedGracefully)
    {
        // KILL! KILL! KILL!
    }
}
唉,我们不能<代码>BackgroundWorker不公开任何强制终止的方式。这是因为它是建立在某些隐藏线程管理系统之上的抽象,如果应用程序的其他部分被强制终止,它可能会破坏应用程序的稳定性

实现上述功能的唯一方法[至少我见过]是管理自己的线程

理想示例

比如说

private Thread worker = null;

// this time, 'Thread' provides all synchronization
// constructs required for main thread to synchronize
// with background task. however, in the interest of
// giving background task a chance to terminate gracefully
// we supply it with this cancel signal
private readonly AutoResetEvent isCanceled = new AutoResentEvent (false);

public void SomeFormEventForStartingBackgroundTask ()
{
    worker = new Thread (BackgroundTask_HotelCalifornia);
    worker.IsBackground = true;
    worker.Name = "Some Background Task"; // always handy to name things!
    worker.Start ();
}

private void BackgroundTask_HotelCalifornia ()
{
    // inspect cancel signal, no wait period
    // 
    // NOTE: so cheating here a bit, this is an instance variable
    // but could as easily be supplied via parameterized thread
    // start delegate
    for ( ; !isCanceled.WaitOne (0);)
    {
    }
}

private void App_FormClosing(object sender, FormClosingEventArgs e)     
{
    // [politely] request termination
    isCanceled.Set ();

    // [politely] wait until background task terminates
    TimeSpan gracePeriod = TimeSpan.FromMilliseconds(100);
    bool isStoppedGracefully = worker.Join (gracePeriod);

    if (!isStoppedGracefully)
    {
        // wipe them out, all of them.
        worker.Abort ();
    }
}
这里有一个关于线程管理的介绍

哪个最适合你?取决于您的应用程序。最好不要动摇局面,修改当前的实现以确保

  • 您的后台任务检查并尊重
    Cancel
    属性
  • 主线程等待完成,而不是轮询
  • 比较和评估每种方法的优缺点非常重要

    如果您必须控制并保证其他人任务的终止,那么编写一个包含上述内容的线程管理系统可能是一个不错的选择。但是,您可能会失去一些现成的功能,如线程池、进度报告、跨线程数据编组[worker会这样做,否?],以及其他一些功能。更不用说,“自己滚动”常常容易出错


    无论如何,希望这有帮助:)

    您不应该运行这样旋转的轮询循环,它将消耗CPU周期,使其他线程更难完成其工作。如果您不能使用WaitHandle或类似的同步原语,并且必须进行轮询,请在其中添加一个Thread.Sleep(x).+1到@Hightechrider的注释中。您的应用程序不仅无法关闭,而且在没有睡眠的情况下,几乎会锁定整个系统。以防万一,您是否已将BackgroundWorker的CanBeCancel或so属性设置为true?@will:是的,
    WorkersSupportScanCellation
    属性已设置。只需执行
    backgroundWorker1.Dispose()
    似乎解决了这个问题,因为我真正想做的就是释放系统资源,因为表单正在关闭。有什么理由不应该在这种情况下使用Dispose成员方法吗?+1当然,除非后台工作线程在关闭之前需要执行某种类型的清理。@Kevin:是的,但由于OP只是试图取消线程,所以听起来好像不是这样……谢谢,Reed。这似乎是一个我把简单的事情做得比实际困难得多的例子。@ReedCopsey关于RCW清理的比赛呢?接近,但不是