This article has been localized into Russian by the community.
Остановка BackgroundWorker
В предыдущей статье было показано, что использование многопоточности позволяет избежать зависания приложения во время выполнения продолжительных операций и отслеживать прогресс выполнения этих операций.
Ещё одной проблемой, с которой можно столкнуться, выполняя задачу в основном потоке приложения, является невозможность остановить выполнение. Причиной этого является то, что основной поток занят выполнением задачи и, следовательно, не обрабатывает события ввода. То есть сколько раз вы не нажимали кнопку "Отмена" или клавишу Esc, это ни к чему не приведет.
К счастью, BackgroundWorker обеспечивает легкую реализацию не только выполнения задач, но и их остановки. Реализация выполнения была рассмотрена в предыдущей статье, в настоящей мы рассмотрим остановку. Рассмотрим пример:
<Window x:Class="WpfTutorialSamples.Misc.BackgroundWorkerCancellationSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="BackgroundWorkerCancellationSample" Height="120" Width="200">
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBlock Name="lblStatus" HorizontalAlignment="Center" Margin="0,10" FontWeight="Bold">Not running...</TextBlock>
<WrapPanel>
<Button Name="btnStart" Width="60" Margin="10,0" Click="btnStart_Click">Start</Button>
<Button Name="btnCancel" Width="60" Click="btnCancel_Click">Cancel</Button>
</WrapPanel>
</StackPanel>
</Window>
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Media;
namespace WpfTutorialSamples.Misc
{
public partial class BackgroundWorkerCancellationSample : Window
{
private BackgroundWorker worker = null;
public BackgroundWorkerCancellationSample()
{
InitializeComponent();
worker = new BackgroundWorker();
worker.WorkerSupportsCancellation = true;
worker.WorkerReportsProgress = true;
worker.DoWork += worker_DoWork;
worker.ProgressChanged += worker_ProgressChanged;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
}
private void btnStart_Click(object sender, RoutedEventArgs e)
{
worker.RunWorkerAsync();
}
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
worker.CancelAsync();
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
for(int i = 0; i <= 100; i++)
{
if(worker.CancellationPending == true)
{
e.Cancel = true;
return;
}
worker.ReportProgress(i);
System.Threading.Thread.Sleep(250);
}
e.Result = 42;
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
lblStatus.Text = "Working... (" + e.ProgressPercentage + "%)";
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if(e.Cancelled)
{
lblStatus.Foreground = Brushes.Red;
lblStatus.Text = "Cancelled by user...";
}
else
{
lblStatus.Foreground = Brushes.Green;
lblStatus.Text = "Done... Calc result: " + e.Result;
}
}
}
}
XAML приложения предельно прост: текстовая метка для отображения состояния и две кнопки для запуска и остановки выполнения задачи.
Написание программного кода начинается с создания экземпляра BackgroundWorker. Обратите внимание на свойства WorkerSupportsCancellation и WorkerReportsProgress. Этим свойствам необходимо присвоить значение true, иначе при попытке использования соответствующих функций будет вброшено исключение.
При нажатии на кнопку "Cancel" вызывается метод CancelAsync(). При этом свойству CancellationPending экземпляра BackgroundWorker присваивается значение true, что является сигналом о том, что пользователь хочет остановить выполнение задачи. Это всё, что можно выполнить в основном потоке приложения. Все остальные действия по остановке необходимо предпринять в обработчике DoWork.
В обработчике события DoWork выполняется счет до 100 с задержкой в 250 миллисекунд на каждой итерации. Таким образом, мы получаем достаточно длительный процесс, что позволяет как отследить прогресс выполнения, так и остановить выполнение.
Свойство CancellationPending проверяется на каждой итерации. При остановке задачи это свойство принимает значение true и происходит выход из цикла. Свойство Cancel аргумента события устанавливается в true для обозначения того, что выполнение было прервано. Этот аргумент используется в событии RunWorkerCompleted и позволяет определить, была задача выполнена полностью или прервана.
В обработчике события RunWorkerCompleted изменяется текст метки состояния в зависимости от того, была задача остановлена или нет.
Заключение
Реализация остановки выполнения задачи с помощью BackgroundWorker достаточно проста: нужно установить значение свойства WorkerSupportsCancellation равным true и проверять свойство CancellationPending в процессе работы. Для остановки выполнения задачи следует просто вызвать метод CancelAsync().