为什么BackgroundWorker总是很忙?

我在WPF应用程序中的后台工作者中发现了一些奇怪的东西。

我现在要完成的是等到BW完成另一个线程。

检查以下代码:

if (bw.IsBusy) { bw.CancelAsync(); System.Threading.ThreadStart WaitThread = new System.Threading.ThreadStart(delegate() { while (bw.IsBusy) { System.Threading.Thread.Sleep(100); } bw.RunWorkerAsync(); }); System.Windows.Application.Current.Dispatcher.Invoke( System.Windows.Threading.DispatcherPriority.Normal, WaitThread); // if I remove this line, bw fires RunWorkerAsyncEvent } else { bw.RunWorkerAsync(); } 

请注意,我添加了Dispatcher.Invoke以等待bw不忙,但如果我调用它并且从不触发RunWorkerAsyncCompleted事件,则所有时间都很忙。 虽然,如果我删除该行,FIRES事件,这真的很奇怪。

我怎么能等到bw结束?

这称为“死锁”,这是一个非常常见的线程问题。 在运行RunWorkerCompleted事件之前,BGW无法停止忙碌。 该事件在您的应用程序的主线程上运行,它只能在您的主线程不忙于执行其他操作时运行。 它必须是空闲的,在调度程序循环内运行。

但是你的主线程没有空闲,它被卡在while()循环内等待IsBusy返回false。 所以事件永远不会运行,因为主线程忙于等待BGW完成,BGW无法完成,因为主线程永远不会空闲。 “致命的拥抱”,又名死锁。

你必须以不同的方式做到这一点,你不能等待。 比如创建另一个BGW实例。 或者只是在RWC事件触发之前不允许此代码运行。 或者通过设置一个标志,由RunWorkerCompleted事件处理程序测试,然后可以再次启动BGW。

我不确定我是否理解正确,但我认为你正在尝试重新启动BackgroundWorker (所以如果它很忙,你先停止它然后再启动它),如果这是你想要的,试试这个:

在你class级的某个地方声明这个委托

  private delegate bool StateChecker(); 

并使用此代码重新启动BackgroundWorker

  StateChecker stillWorking = () => { return bw.IsBusy; }; if (bw.IsBusy) { bw.CancelAsync(); while ((bool)this.Dispatcher.Invoke(stillWorking, null)) Thread.Sleep(15); } bw.RunWorkerAsync(); 

由于应用程序的“主”线程无法运行(并处理事件)而发生死锁,因此Backgroundworker永远无法触发它的完成函数。 你可以做什么来让应用程序触发这个是添加一个Application.DoEvents();

这就是我目前在一个类似场景中使用的东西,它看起来像一个魅力!

  while (bw.IsBusy) { Application.DoEvents(); System.Threading.Thread.Sleep(100); } 

这是完整且有效的代码。

C#(窗口类)

  public partial class ForceSyncWindow : Window { BackgroundWorker backgroundWorker = new BackgroundWorker(); public ForceSyncWindow() { InitializeComponent(); ProgressBar1.Visibility = System.Windows.Visibility.Hidden; backgroundWorker.WorkerSupportsCancellation = true; // To report progress from the background worker we need to set this property backgroundWorker.WorkerReportsProgress = true; // This event will be raised on the worker thread when the worker starts backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork); // This event will be raised when we call ReportProgress backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker_ProgressChanged); backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted; } void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { // First, handle the case where an exception was thrown. if (e.Error != null) { MessageBox.Show(e.Error.Message); } else if (e.Cancelled) { // Next, handle the case where the user canceled // the operation. // Note that due to a race condition in // the DoWork event handler, the Cancelled // flag may not have been set, even though // CancelAsync was called. ProgressBar1.Value = 0; // TODO LOG = "Canceled"; } else { // Finally, handle the case where the operation // succeeded. // TODO LOG e.Result.ToString(); } ProgressBar1.Value = 0; ProgressBar1.Visibility = System.Windows.Visibility.Hidden; // Enable the Synchronize button. this.Synchronize.IsEnabled = true; // Disable the Cancel button. this.Cancel.IsEnabled = false; } // On worker thread so do our thing! void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) { // Your background task goes here for (int i = 0; i <= 100; i++) { if (backgroundWorker.CancellationPending == true) { e.Cancel = true; break; } else { // Perform a time consuming operation and report progress. // Report progress to 'UI' thread backgroundWorker.ReportProgress(i); // Simulate long task System.Threading.Thread.Sleep(100); ; } } } // Back on the 'UI' thread so we can update the progress bar void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) { // The progress percentage is a property of e ProgressBar1.Value = e.ProgressPercentage; } private void Synchronize_Click(object sender, RoutedEventArgs e) { ProgressBar1.Value = 0; ProgressBar1.Visibility = System.Windows.Visibility.Visible; // Disable the Synchronize button. this.Synchronize.IsEnabled = false; // Enable the Cancel button. this.Cancel.IsEnabled = true; // Start the background worker backgroundWorker.RunWorkerAsync(); } private void Cancel_Click(object sender, RoutedEventArgs e) { if (backgroundWorker.IsBusy) { // Cancel the background worker backgroundWorker.CancelAsync(); } } } 

XAML