私は2つのクラスを持ってい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
。