Using Events and BackgroundWorker to Update a UI Invoking Race Conditions

I have two classes: Appleand Dog. Both of these classes have a method called at startup DoLotsOfWork()that publishes an event ProgressChanged.

public class Apple
{
    //For progress bars
    public event EventHandler<ProgressChangedEventArgs> ProgressChanged;
    private void OnProgressChanged(double progress)
    {
        if(ProgressChanged != null)
            ProgressChanged(this, new ProgressChangedEventArgs((int)(progress * 100), null));
    }

    //Takes a long time to run
    public void DoLotsOfWork()
    {
        for(lots of things)
        {
            ...
            OnProgressChanged(percentage);
        }
    }
}
//Dog is similar

To make the user interface lock, I run it with BackgroundWorker. I have Apple.ProgressChangedboth a Dog.ProgressChangedcall BackgroundWorker.ReportProgress(which triggers the event BackgroundWorker.ProgressChanged) to update the label and progress bar so that the user knows what is happening.

public class MainForm : Form
{
    private Apple _apple;
    private Dog _dog;
    private bool _isAppleCompleted;

    ...

    //Set the ProgressChanged callbacks and start the BackgroundWorker
    private void MainForm_Load(object sender, EventArgs e)
    {
        _apple.ProgressChanged += (a, args) => backgroundWorker1.ReportProgress(args.ProgressPercentage);
        _dog.ProgressChanged += (a, args) => backgroundWorker1.ReportProgress(args.ProgressPercentage);
        backgroundWorker1.RunWorkerAsync();
    }

    //Invoke the UI thread to set the status/progress
    private void SetStatus(string status)
    {
        lblStatus.Invoke((Action)(() => lblStatus.Text = status));
    }
    private void SetProgress(int progress)
    {
        progressBar.Invoke((Action)(() => progressBar.Value = progress));
    }

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        _isAppleCompleted = false;
        SetStatus("Apple (Step 1/2)");
        _apple.DoLotsOfWork();

        //Thread.Sleep(1); //This *sometimes* fixes the problem!?

        _isAppleCompleted = true;
        SetStatus("Dog (Step 2/2)");
        _dog.DoLotsOfWork();
    }

    private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        //_apple.DoLotsOfWork should cause the progress bar to go from 0 to 50
        //_dog.DoLotsOfWork should cause the progress bar to go from 50 to 100
        int progress = (_isAppleCompleted ? 50 : 0) + e.ProgressPercentage/2;
        SetProgress(progress);
    }

    private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        //stuff
    }
}

What I expect: Text "Apple (step 1/2)" as the progress bar moves from 0% to 50%. Then the phrase “Dog (step 2/2)” is displayed when the progress bar moves from 50% to 100%.

: "Dog ( 2/2)". 0% 100%, 50% 100%.


, , ; , Control.Invoke() Action, , - , . - , , ?

, , 0 <= e.ProgressPercentage <= 100 progressBar.Maximum = 100.

+3
2

, , BackgroundWorker ProgressChanged , , backgroundWorker1.ReportProgress ProgressChanged (, , ), . , Invoke SetProgress .

+1

Invoke() , . : 1. BackgroundWorker ReportProgress.
2. BackgroundWorker WinForm ProgressChanged .

0

All Articles