C# BackgroundWorker - как мне избавиться от DoEvents
Я пытаюсь найти лучший способ справиться с фоновым работником, который срабатывает от щелчков переключателей. Я создал очень простую форму с 3-мя переключателями и ярлыком. Все переключатели имеют одно и то же событие radioButton_CheckedChanged. Если событие завершается, я обновляю метку "Завершено". Если вы нажмете другую кнопку-переключатель до завершения события, обновите метку до Отменено. Ниже приведен код, который я написал в этом быстром примере. Хотя приложение работает так, как и ожидалось, меня беспокоит использование Application.DoEvents. Каковы мои альтернативы этому. По понятным причинам я не могу спать пока IsBusy. Я все это делаю неправильно или есть лучший способ сделать это? Спасибо, поко
private void radioButton_CheckedChanged(object sender, EventArgs e)
{
RadioButton rb = sender as RadioButton;
if (rb.Checked)
{
if (backgroundWorker1.IsBusy)
{
backgroundWorker1.CancelAsync();
while (backgroundWorker1.IsBusy)
Application.DoEvents();
}
backgroundWorker1.RunWorkerAsync();
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 0; i < 100 && !worker.CancellationPending; ++i)
Thread.Sleep(1);
if (worker.CancellationPending)
{
e.Cancel = true;
return;
}
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
label1.Text = "Canceled";
else
label1.Text = "Complete";
}
2 ответа
Вы должны переместить код, который должен выполняться после завершения BackgroundWorker, в обработчик RunWorkerCompleted. В псевдокоде:
private void radioButton_CheckedChanged(object sender, EventArgs e)
{
// ...
if (backgroundWorker1.IsBusy)
{
backgroundWorker1.CancelAsync();
addJobToQueue(); // Don't wait here, just store what needs to be executed.
} else {
backgroundWorker1.RunWorkerAsync();
}
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled) {
label1.Text = "Canceled";
}
else {
label1.Text = "Complete";
}
// We've finished! See if there is more to do...
if (thereIsAnotherJobInTheQueue())
{
startAnotherBackgroundWorkerTask();
}
}
DoEvents
не следует принимать так случайно. Есть лучшие способы. Один из очень хороших описан здесь в SO. Этот ответ, вероятно, лучше для вас.
Следовательно, ваше решение становится:
private AutoResetEvent _resetEvent = new AutoResetEvent(false);
private void radioButton_CheckedChanged(object sender, EventArgs e)
{
RadioButton rb = sender as RadioButton;
if (rb.Checked)
{
if (backgroundWorker1.IsBusy)
{
backgroundWorker1.CancelAsync();
_resetEvent.WaitOne(); // will block until _resetEvent.Set() call made
}
backgroundWorker1.RunWorkerAsync();
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 0; i < 100 && !worker.CancellationPending; ++i)
Thread.Sleep(1);
if (worker.CancellationPending)
{
e.Cancel = true;
}
_resetEvent.Set();
}