C # BackgroundWorker - как мне избавиться от DoEvents - PullRequest
1 голос
/ 29 октября 2010

Я пытаюсь найти лучший способ справиться с фоновым работником, который срабатывает от щелчков переключателей.Я создал очень простую форму с 3-мя переключателями и ярлыком.Все переключатели имеют одно и то же событие radioButton_CheckedChanged.Если событие завершается, я обновляю метку «Завершено».Если вы нажмете другую кнопку-переключатель до завершения события, обновите метку до Отменено.Ниже приведен код, который я написал в этом быстром примере.Хотя приложение имеет тенденцию работать должным образом, меня беспокоит использование Application.DoEvents.Каковы мои альтернативы этому.По понятным причинам я не могу спать пока IsBusy.Я все делаю неправильно или есть лучший способ сделать это?Спасибо, poco

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 ]

7 голосов
/ 29 октября 2010

Вы должны переместить код, который должен выполняться при завершении 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();
    }
}
0 голосов
/ 29 октября 2010

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();
}
...