Why does BackgroundWorker in WPF need Thread.Sleep to update user interface controls?

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;
    }
}
}
+3
source share
2 answers

Because in your (artificial) scenario, you are pumping 1000 requests for updates to the main thread.
No time is required for the downtime of the cycle (it is necessary to update the screen).

But (thanks to TerrorAustralis), you should start by merging the bgWorker_ReportProgress and myProgressReporter methods. You are now syncing twice, which is a possible cause of stackoverflow. Sending UpdateProgress events is one of the main functions of 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
}
+5

Posibility:
Dispatcher.BeginInvoke() - . , , . , , Dispatcher.Invoke(),

, , backgroundWorker ProgressChanged .

+3

Source: https://habr.com/ru/post/1749493/


All Articles