Который раз о фоновом потоке

    • WinForms
    • .NET
    • C#
    • BackrgoundWorker
  • modified:
  • reading: 3 minutes

Очень часто люди выдумывают уже сделанное, а невнимательность заставляет писать намного больше кода, чем это необходимо. Рассказ про прекрасный компонент BackgroundWorker.

Навеяло написание данной статьи не раз увиденные мной статьи о том как сделать фоновый процесс на вашей форме, как, например, эти:

http://blogs.gotdotnet.ru/personal/filippovma/CategoryView.aspx?category=.NET+GUI

И статьи, описанные с заголовком "Простая и безопасная реализации многопоточности в Windows Forms". Я не хочу сказать, что в них что-то не верно, просто есть методы, наверное, попроще. А те статьи обязательно нужно прочитать, чтобы понимать вообще о реализации многопоточности и с какими ошибками можно столкнуться при ее осуществлении, кто писал - тот знает ;).

Существует такой компонент, как BackgroundWorker, он существует в WinForms начиная с .NET Framework 2.0.

Он позволяет вызывать некоторую операцию в отдельном потоке, например это бывает необходимо при долгих вычислениях, или множественных и продолжительных запросах к базе данных, ну и вообще еще может быть много назначений.

Начнем с самого начала.

Объявляем сам объект и инициализируем его свойства:

this.backgroundWorker.WorkerReportsProgress = true;
this.backgroundWorker.WorkerSupportsCancellation = true;
this.backgroundWorker.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker_DoWork);
this.backgroundWorker.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker_RunWorkerCompleted);
this.backgroundWorker.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.backgroundWorker_ProgressChanged);

DoWork - это что у нас будет делать фоновый поток, для примера просто будем вызывать Sleep у текущего (фонового) потока:

void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
       for (int i = 1; i <= 100; i++)
       {
            if (backgroundWorker.CancellationPending)
                return;
            Thread.Sleep(1000);
            backgroundWorker.ReportProgress(i);
       }
}

Хочу обратить внимание, на то что я в цикле так же проверяю "backgroundWorker.CancellationPending" - это необходимо для того если этому процессу сказано остановиться. Далее в каждом цикле я вызываю метод у этого самого backgroundWorker под названием ReportProgress, которому я передаю процент выполняения потока (так же можно передавать и любой другой объект - к примеру, для отображения progressBar в своем диапазоне от 1 до 10000, для этого можно передать второй параметр).

Следующее, если хотим следить за тем что у нас происходит с потоком (прослеживать тот же процент выполнения), то для этого мы прикрепили метод к событию ProgressChanged:

void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
        progressBar.Value = e.ProgressPercentage;
}

В котором мы просто выставляем значение прогресс бару процента выполнения потока.

Ну а по окончании потока цепляем метод на событие RunWorkerCompleted, который, к примеру, просто сообщит что-то прекрасное:

void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
       MessageBox.Show(this, "Hello word!!!");
}

Раз упоминул про то, что поток можно остановить, ведь не просто так мы проверяли backgroundWorker.CancellationPending, то покажу как в нашем случае это делается, навесим на обычную кнопку метод на событие OnClick:

private void buttonCancel_Click(object sender, EventArgs e)
{
       backgroundWorker.CancelAsync();
}

Ну а для того, чтобы запустить поток необходимо просто вызвать метод:

backgroundWorker.RunWorkerAsyn​c();

Этот пример намного проще описанного в MSDN. Там есть пример с показом того, как можно передавать некоторые результаты между всеми методами, участниками фонового потока, а моя статья направлена просто на обращения внимания на компонент BackgroundWorker.