为什么WPF中的BackgroundWorker需要Thread.Sleep来更新UI控件?

Syl*_*ler 3 c# wpf backgroundworker progress-bar

namespace WpfApplication1
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
    BackgroundWorker bgWorker;
    Action<int> myProgressReporter;

    public Window1()
    {
        InitializeComponent();
        bgWorker = new BackgroundWorker();
        bgWorker.DoWork += bgWorker_Task;
        bgWorker.RunWorkerCompleted += myWorker_RunWorkerCompleted;

        // hook event to method
        bgWorker.ProgressChanged += bgWorker_ReportProgress;

        // hook the delegate to the method
        myProgressReporter = updateProgress;

        bgWorker.WorkerReportsProgress = true;

    }

    private void myWorker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
    {
        object result;
        result = e.Result;
        MessageBox.Show(result.ToString());
        progressBar1.Value = 0;
        button1.IsEnabled = true;
    }

    private void bgWorker_ReportProgress(object sender, ProgressChangedEventArgs e)
    {
        System.Windows.Threading.Dispatcher disp = button1.Dispatcher;
        disp.BeginInvoke(myProgressReporter,e.ProgressPercentage);
        //Dispatcher.BeginInvoke(myProgressReporter, DispatcherPriority.Normal, e.ProgressPercentage);
    }

    private void updateProgress(int progressPercentage)
    {
        progressBar1.Value = progressPercentage;
    }

    private void bgWorker_Task(Object sender, DoWorkEventArgs e)
    {
        int total = 1000;
        for (int i = 1; i <= total; i++)
        {
            if (bgWorker.WorkerReportsProgress)
            {
                int p = (int)(((float)i / (float)total) * 100);
                bgWorker.ReportProgress(p);
            }
            Thread.Sleep(1); // Without Thread.Sleep(x) the main thread freezes or gives stackoverflow exception, 
        }

        e.Result = "Completed";
    }

    private void button1_Click(object sender, RoutedEventArgs e)
    {            
        if(!bgWorker.IsBusy)
            bgWorker.RunWorkerAsync("This is a background process");
        button1.IsEnabled = false;
    }
}
}
Run Code Online (Sandbox Code Playgroud)

Hen*_*man 5

因为在您的(人工)场景中,您需要为主线程提供1000个请求更新.
它没有时间进行空闲循环(进行屏幕更新所必需的).

但是(感谢TerrorAustralis),您应该首先合并bgWorker_ReportProgress和myProgressReporter方法.您现在正在同步两次,这可能是stackoverflow的原因.调度UpdateProgress事件是Backgroundworker的主要功能之一:

private void bgWorker_ReportProgress(object sender, ProgressChangedEventArgs e)
{
  //System.Windows.Threading.Dispatcher disp = button1.Dispatcher;
  //disp.BeginInvoke(myProgressReporter,e.ProgressPercentage);
  progressBar1.Value = progressPercentage; // safe because we're on the main Thread here
}
Run Code Online (Sandbox Code Playgroud)