我有两个类,AppleDog。这两个类都有一个在启动时称为DoLotsOfWork()的方法,该方法发布事件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


为了防止UI锁定,我使用BackgroundWorker运行它们。我有Apple.ProgressChangedDog.ProgressChanged调用BackgroundWorker.ReportProgress(它调用BackgroundWorker.ProgressChanged事件)来更新标签和进度条,因此用户知道发生了什么。

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
    }
}


我预期会发生什么:进度条从0%移至50%时显示文本“ Apple(第1/2步)”。然后,随着进度条从50%移至100%,将显示短语“狗(步骤2/2)”。

实际发生的情况:仅显示文本“ Dog(步骤2/2)”。进度条从0%到100%,然后又回到50%并上升到100%。



由于我认为事件处理程序与事件调用程序在同一线程上运行;我以为Control.Invoke()会阻塞直到Action完成为止,我不知道怎么可能出现任何竞争状况,因为一切基本上都是同步发生的。有谁知道为什么会发生这种情况,以及如何解决?

是的,我检查了0 <= e.ProgressPercentage <= 100progressBar.Maximum = 100

最佳答案

您在底部的假设是正确的,尽管您应注意,BackgroundWorker将在创建控件的线程上引发ProgressChanged,所以从辅助线程调用backgroundWorker1.ReportProgress会在UI线程上调用ProgressChanged处理程序(假设您在其中创建了背景工作人员),因此并非所有事情都是同步发生的。这也意味着不需要在您的私有SetProgress方法中进行调用。

08-05 14:04