我有两个班,Apple
和Dog
。这两个类都有一个在启动时调用的方法,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.ProgressChanged
并Dog.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 (Step 1/2)” 。然后,随着进度条从 50% 移动到 100%,会显示短语“Dog (Step 2/2)”。
实际发生的情况:只显示文本“Dog (Step 2/2)”。进度条从 0% 变为 100%,然后返回 50% 并向上移动到 100%。
因为我认为事件处理程序与事件调用者在同一个线程上运行;而且我认为 Control.Invoke()
在完成之前会阻塞Action
,我看不出怎么会有任何竞争条件,因为一切基本上都是同步发生的。 有谁知道为什么会发生这种情况,以及如何解决它?
是的,我检查了0 <= e.ProgressPercentage <= 100
, 和progressBar.Maximum = 100
.