当我的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);
}
}
我还应该做些什么吗?
答案 0 :(得分:7)
一些非常好的建议,但我不认为它们解决了潜在的问题:取消后台任务。
不幸的是,使用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 ();
}
}
那就是对线程管理的一个不错的介绍。
哪种最适合你?取决于您的申请。可能最好不摇摆船,并修改您当前的实施以确保
Cancel
属性比较和评估每种方法的优缺点非常重要。
如果您必须控制并保证其他人的任务的终止,那么编写包含上述内容的线程管理系统可能就是您的选择。但是你会失去开箱即用的功能,如线程池,进度报告,跨线程数据编组[工作人员那样做,没有?],以及其他一些东西。更不用说“滚动你自己”往往容易出错。
无论如何,希望这有助于:)
答案 1 :(得分:4)
在后台工作线程中,您需要检查BackgroundWorker.CancellationPending标志,如果为真,则退出。
CancelAsync()只设置此标志。
或者换句话说。 CancelAsync()实际上并没有取消任何东西。它不会中止线程或导致它退出。如果工作线程处于循环中并定期检查CancellationPending标志,它可以捕获取消请求并退出。
MSDN有一个示例here,尽管它在工作程序中没有使用循环。
答案 2 :(得分:4)
Kevin Gale说明你的BackgroundWorker的DoWork处理程序需要轮询CancellationPending并在请求取消时返回。
话虽如此,如果您的应用程序关闭时发生这种情况,您也可以安全地忽略它。 BackgroundWorker使用ThreadPool线程,根据定义,该线程是后台线程。保持此运行不会阻止您的应用程序终止,并且当您的应用程序关闭时,该线程将自动被拆除。
答案 3 :(得分:1)
当BGW仍在运行时,此代码保证死锁。在RunWorkerCompleted事件完成运行之前,BGW无法完成。在UI线程空闲并运行消息循环之前,RunWorkerCompleted无法运行。但是UI线程不是空闲的,而是停留在while循环中。
如果您希望BGW线程干净利落,您 以保持您的表单生动。检查this thread以了解如何执行此操作。
答案 4 :(得分:0)
尝试:
if (this.backgroundWorker1.IsBusy) this.backgroundWorker1.CancelAsync();