【问题标题】:Why BackgroundWorker always is busy?为什么BackgroundWorker总是很忙?
【发布时间】:2013-01-21 00:34:20
【问题描述】:

我在我的 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 事件,则所有时间都是忙碌的。虽然,如果我删除那条线,会触发事件,这真的很奇怪。

我怎样才能等到 bw 完成?

【问题讨论】:

  • 不清楚,你删除了哪一行,然后又发生了哪些变化……你能解释一下吗?

标签: c# wpf backgroundworker dispatcher dispatch


【解决方案1】:

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

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

您将不得不以不同的方式执行此操作,您不能等待。通过创建另一个 BGW 实例来说。或者将等待后的代码移动到 RunWorkerCompleted 事件处理程序中。如果它是由按钮的 Click 事件激活的,那么请确保在调用 RunWorkerAsync() 时禁用该按钮,然后在 RunWorkerCompleted 中重新启用它。

【讨论】:

  • 在我的 BGW 完成之前,我还有其他选择吗
  • 另一个肮脏的解决方案:如果等待循环在 MainThread:调用“Application.DoEvents();”在循环。这也将解决死锁。
【解决方案2】:

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

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

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

【讨论】:

  • 是否有 WPF 原生版本的 Application.DoEvents?该命名空间通常与 winforms 相关联,通常不包含在 WPF 应用程序中。
【解决方案3】:

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

在你的班级某处声明这个委托

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

【讨论】:

    【解决方案4】:

    这是完整且有效的代码。

    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

    <Window x:Class="MySyncManager.Views.ForceSyncWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    
            Title="ForceSyncWindow" Height="300" Width="509" ResizeMode="NoResize" WindowStartupLocation="CenterScreen">
        <Grid>
    
            <Button Content="Synchronize" Name="Synchronize" HorizontalAlignment="Left" Margin="411,10,0,0" VerticalAlignment="Top" Width="75" Click="Synchronize_Click"/>
            <RichTextBox HorizontalAlignment="Left" Height="132" Margin="10,116,0,0" VerticalAlignment="Top" Width="476">
    
            </RichTextBox>
            <Button Content="Cancel" x:Name="Cancel" HorizontalAlignment="Left" Margin="411,40,0,0" VerticalAlignment="Top" Width="75" RenderTransformOrigin="0.508,2.154" Click="Cancel_Click"/>
            <ProgressBar Name="ProgressBar1" HorizontalAlignment="Left" Height="10" Margin="10,101,0,0" VerticalAlignment="Top" Width="476"/>
    
        </Grid>
    </Window>
    

    【讨论】:

      【解决方案5】:

      把它放在另一个线程里面:

      private void myButton_Click(object sender, RoutedEventArgs e)
      {
         BackgroundWorker mainWorker = new BackgroundWorker();
         mainWorker.DoWork += new DoWorkEventHandler(mainWorker_DoWork);
         mainWorker.RunWorkerAsync();
      }
      
      void mainWorker_DoWork(object sender, DoWorkEventArgs e)
      {
         for(int x = 0; x >= 100; x++)
         {
            BackgroundWorker worker = new BackgroundWorker();
            worker.DoWork += new DoWorkEventHandler(worker_DoWork);
            worker.RunWorkerAsync(argument: x);
            while(worker.IsBusy)
            {
               Thread.Sleep(100);
            }
         }
      }
      
      void worker_DoWork(object sender, DoWorkEventArgs e)
      {
         string iam = "Hello i'm thread number: " + e.Argument.ToString();
         //do something ...
      }
      

      【讨论】:

        猜你喜欢
        • 2023-03-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-01-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多