取消后台任务

当我的C#应用​​程序关闭时,它有时会被清理程序捕获。 具体来说,后台工作者没有关闭。 这基本上就是我试图关闭它的方式:

private void App_FormClosing(object sender,FormClosingEventArgs e){backgroundWorker1.CancelAsync(); 而(backgroundWorker1.IsBusy); //被困在这里 }

我应该采用不同的方式吗? 我使用的是Microsoft Visual C#2008 Express Edition。 谢谢。

附加信息:

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

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

我还应该做些什么吗?

Kevin Gale说明你的BackgroundWorker的DoWork处理程序需要轮询CancellationPending并在请求取消时返回是正确的。

话虽如此,如果您的应用程序关闭时发生这种情况,您也可以安全地忽略它。 BackgroundWorker使用ThreadPool线程,根据定义,该线程是后台线程。 保持此运行不会阻止您的应用程序终止,并且当您的应用程序关闭时,该线程将自动被拆除。

一些非常好的建议,但我不相信它们解决了潜在的问题:取消后台任务。

不幸的是,使用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); } 

这是默认情况下发生的事情。 现在,也许你的任务不是一个无限循环,也许它只是一个长期运行的任务。 无论哪种方式,您的主线程将阻止[实际上它正在旋转,但是whatevs]直到任务完成,或者不是视情况而定。

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

示例改进

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

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

那就是线程管理的一个不错的介绍。

哪个最适合你? 取决于您的申请。 最好不要晃动船只,并修改您当前的实施以确保这一点

  1. 您的后台任务检查并尊重Cancel属性
  2. 你的主线程等待完成,而不是轮询

比较和评估每种方法的优缺点非常重要。

如果您必须控制并保证终止其他人的任务,那么编写包含上述内容的线程管理系统可能就是您的选择。 但是你会失去开箱即用的function,如线程池,进度报告,跨线程数据编组[工作人员那样做,没有?],以及其他一些东西。 更不用说,“滚动你自己”往往容易出错。

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

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

CancelAsync()只设置此标志。

或者用另一种方式。 CancelAsync()实际上并没有取消任何东西。 它不会中止线程或导致它退出。 如果工作线程处于循环中并定期检查CancellationPending标志,则它可以捕获取消请求并退出。

MSDN 在这里有一个例子,虽然它在工作程序中没有使用循环。

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

如果你想让BGW线程干净利落,你必须让你的表格保持活力。 检查此线程以了解如何执行此操作。

尝试:

 if (this.backgroundWorker1.IsBusy) this.backgroundWorker1.CancelAsync();