С windows forms backgroundworker

Класс BackgroundWorker

Выполнять асинхронные операции можно разными способами. Ранее уже был показан один бесхитростный подход — создание нового объекта System.Threading.Thread вручную, применение асинхронного кода и запуск его методом Thread.Start(). Это мощный подход, потому что объект Thread ничего не задерживает. Можно создавать десятки потоков, устанавливать их приоритеты, управлять их состоянием (например, приостанавливать, возобновлять или прерывать их) и т.д.

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

Приемы написания хорошего многопоточного кода и используемые при этом классы .NET не являются специфичными для WPF. В мире WPF может использоваться многопоточный код, аналогичный тому, что применяется в приложениях Windows Forms. Здесь же рассматривается один из наиболее простых и безопасных подходов: компонент System.ComponentModel.BackgroundWorker.

Класс BackgroundWorker появился в .NET 2.0 и был предназначен для упрощения работы с потоками в приложениях Windows Forms. Однако BackgroundWorker в той же мере применим и в WPF. Компонент BackgroundWorker предоставляет почти идеальный способ запуска длительно выполняющихся задач в отдельном потоке. Он использует диспетчер «за кулисами» и абстрагирует сложности маршализации с помощью модели событий.

Как вы убедитесь, BackgroundWorker также поддерживает два дополнительных удобства: события продвижения и сообщения отмены. В обоих случаях детали многопоточности скрыты, что облегчает кодирование.

BackgroundWorker незаменим, если есть единственная асинхронная задача, которая выполняется в фоновом режиме от начала до конца (с необязательной поддержкой уведомлений о продвижении и возможностью отмены). Если же имеется в виду что-то еще, например, асинхронная задача, которая работает на протяжении всей жизни приложения, или асинхронная задача, взаимодействующая с приложением, пока оно выполняет свою работу, то придется спроектировать специальное решение, воспользовавшись поддержкой многопоточности .NET.

Простая асинхронная операция

Чтобы попробовать BackgroundWorker в действии, стоит рассмотреть пример приложения. Базовым ингредиентом любого тестирования является длительно выполняющийся процесс. В следующем примере используется распространенный алгоритм нахождения простых чисел в заданном диапазоне, называемый решетом Эратосфена, который был изобретен Эратосфеном примерно в 240 г. до нашей эры.

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

Ниже приведена разметка окна и исходный код:

Метод FindPrimes() принимает два параметра, которые ограничивают диапазон чисел. Затем код возвращает массив целых чисел, содержащий все простые числа из заданного диапазона.

Создание BackgroundWorker

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

Можно создать BackgroundWorker в коде и присоединить программно все обработчики событий.

Читайте также:  Synology audio station для windows

Можно объявить BackgroundWorker в XAML-разметке. Преимущество такого подхода в возможности присоединения обработчиков событий через атрибуты. Поскольку BackgroundWorker не является видимым элементом WPF, его нельзя поместить куда угодно. Вместо этого его понадобится объявить как ресурс для окна.

Оба подхода эквивалентны. Мы будем использовать второй подход. Первый шаг предусматривает обеспечение доступа к пространству имен System.ComponentModel в XAML-документе через импорт. Чтобы сделать это, понадобится отобразить пространство имен на префикс XML:

Теперь можно создать экземпляр BackgroundWorker в коллекции Windows.Resources. При этом должно быть указано ключевое имя, чтобы позже можно было извлечь этот объект. В данном примере ключевым именем является backgroundWorker:

Преимущество объявления BackgroundWorker в разделе Window.Resources заключается в том, что можно установить его свойства и присоединить обработчики событий посредством атрибутов.

Например, ниже приведен дескриптор BackgroundWorker, который получится в конце примера, включающий поддержку уведомления о прохождении и возможности отмены, а также присоединяющий обработчики событий к DoWork, ProgressChanged и RunWorkerCompleted:

Чтобы получить доступ к этому ресурсу в коде, потребуется поместить его в коллекцию Resources. В данном примере окно выполняет этот шаг в своем конструкторе, так что весь код обработки событий легко доступен:

Запуск BackgroundWorker

Первый шаг к использованию BackgroundWorker с примером поиска простых чисел состоит в создании специального класса, который позволит передать входные параметры BackgroundWorker. При вызове BackgroundWorker.RunWorkerAsync() можете указать любой объект, который будет доставлен событию DoWorк. Тем не менее, допускается задавать только один объект, поэтому придется упаковать числа начала и конца диапазона в один класс, как показано ниже:

Чтобы запустить сам BackgroundWorker, понадобится вызвать метод RunWorkerAsync() и передать объект FindPrimesInput. Ниже приведен код, который делает это, когда пользователь щелкает на кнопке Найти простые числа:

Когда BackgroundWorker начинает работу, он захватывает свободный поток из пула потоков CLR и затем инициирует событие DoWork из этого потока. В обработчике события DoWork запускается длительно выполняющаяся задача. Однако следует соблюдать осторожность и не обращаться к разделяемым данным (таким как поля класса окна) или объектам пользовательского интерфейса.

Как только работа будет завершена, BackgroundWorker инициирует событие RunWorkerCompleted, чтобы уведомить приложение. Это событие инициируется в потоке диспетчера, что позволит обратиться к разделяемым данным и пользовательскому интерфейсу, не порождая никаких проблем.

Как только BackgroundWorker захватывает поток, он инициирует событие DoWork. Это событие можно обработать, вызвав метод Worker.FindPrimes(). Событие DoWork предоставляет объект DoWorkEventArgs, который является ключевым ингредиентом при извлечении и возврате информации. Входной объект извлекается через свойство DoWorkEventArgs.Argument, а результат возвращается за счет установки свойства DoWorkEventArgs.Result:

Сам класс Worker, в котором рассчитываются простые числа, выглядит следующим образом:

По завершении метода DoWork, BackgroundWorker инициирует RunWorkerCompletedEventArgs в потоке диспетчера. В этой точке можно извлечь результат из свойства Result. Затем можно обновить интерфейс и обратиться к переменным уровня окна без опаски:

Обратите внимание, что не понадобился никакой код блокировки, и не было необходимости в использовании метода Dispatcher.BeginInvoke(). Объект BackgroundWorker обо всем позаботился сам.

«За кулисами» BackgroundWorker использует несколько многопоточных классов, которые появились в .NET 2.0, в том числе AsyncOperationManager, AsyncOperation и SynchronizationContext.

По сути, BackgroundWorker применяет AsyncOperationManager для управления фоновой задачей. AsyncOperationManager обладает встроенным интеллектом, а именно: он способен получить контекст синхронизации для текущего потока. В приложении Windows Forms AsyncOperationManager получает объект WindowsFormsSynchronizationContext, в то время как приложение WPF получает объект DispatcherSynchronizationContext. Концептуально эти классы выполняют одинаковую работу, но их внутреннее устройство отличается.

Читайте также:  The run ��� mac os x

Отслеживание продвижения

BackgroundWorker также предоставляет встроенную поддержку первоначальной установки свойства BackgroundWorker.WorkerReportsProgress в true. На самом деле предоставление и отображение информации о продвижении — двухшаговый процесс.

Первым делом коду обработки события DoWork необходимо вызвать метод BackgroundWorker.ReportProgress() и показать предполагаемый процент готовности (от 0% до 100%). Это можно делать редко или часто — как нравится. При каждом вызове ReportProgress() объект BackgroundWorker инициирует событие ProgressChanged. На это событие можно отреагировать, чтобы прочитать процент готовности и обновить пользовательский интерфейс. Поскольку событие ProgressChanged инициировано в потоке пользовательского интерфейса, в применении Dispatcher.BeginInvoke() нет необходимости.

Метод FindPrimes() сообщает о продвижении с приращением 1%, используя код вроде показанного ниже:

Как только свойство BackgroundWorker.WorkerReportsProgress установлено, с этого момента можно реагировать на уведомления о продвижении, обрабатывая событие ProgressChanged. В этом примере индикатор продвижения обновляется соответствующим образом:

Придется также видоизменить обработчик события DoWork включив передачу методу FindPrimes() объекта BackgroundWorker. На рисунке ниже показан индикатор продвижения:

Поддержка отмены

С помощью BackgroundWorker столь же просто добавить поддержку отмены длительно выполняющейся задачи. Первый шаг состоит в установке в true свойства BackgroundWorker.WorkerSupportsCancellation.

Чтобы запросить отмену, код должен вызвать метод BackgroundWorker.CancelAsync(). В этом примере отмена запрашивается при щелчке на кнопке Остановить:

При вызове CancelAsync() ничего автоматически не происходит. Вместо этого код, выполняющий задачу, должен явно проверить запрос на отмену, выполнить необходимую очистку и вернуть управление. Ниже приведен код метода FindPrimes(), который проверяет запросы на отмену непосредственно перед сообщением о продвижении:

Код в обработчике события DoWork также должен явно установить свойство DoWorkEventArgs.Cancel в true, чтобы завершить отмену. Затем производится возврат из метода без попытки построить строку простых чисел:

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

Теперь компонент BackgroundWorker позволяет запускать поиск и останавливать его принудительно.

Компонент BackgroundWorker BackgroundWorker Component

BackgroundWorker Компонент позволяет форме или элементу управления выполнять операцию асинхронно. The BackgroundWorker component enables your form or control to run an operation asynchronously.

в этом разделе In This Section

Общие сведения о компоненте BackgroundWorker BackgroundWorker Component Overview
Описывает BackgroundWorker компонент, который дает возможность асинхронно выполнять длительные операции («в фоновом режиме») в потоке, отличном от основного потока пользовательского интерфейса приложения. Describes the BackgroundWorker component, which gives you the ability to execute time-consuming operations asynchronously («in the background»), on a thread different from your application’s main UI thread.

Пошаговое руководство. Фоновое выполнение операции Walkthrough: Running an Operation in the Background
Демонстрирует использование BackgroundWorker компонента в конструкторе для выполнения длительной операции в отдельном потоке. Demonstrates how to use the BackgroundWorker component in the designer to run a time-consuming operation on a separate thread.

Читайте также:  Все команды screen linux

Практическое руководство. Фоновое выполнение операции How to: Run an Operation in the Background
Демонстрирует использование BackgroundWorker компонента для выполнения длительной операции в отдельном потоке. Demonstrates how to use the BackgroundWorker component to run a time-consuming operation on a separate thread.

Пошаговое руководство. Реализация формы, в которой выполняется фоновая операция Walkthrough: Implementing a Form That Uses a Background Operation
Создает приложение с помощью конструктора, который выполняет математические вычисления в асинхронном режиме. Creates an application using the designer that does mathematical computations asynchronously.

Практическое руководство. Реализация формы, в которой выполняется фоновая операция How to: Implement a Form That Uses a Background Operation
Создает приложение, которое выполняет математические вычисления в асинхронном режиме. Creates an application that does mathematical computations asynchronously.

Практическое руководство. Фоновая загрузка файла How to: Download a File in the Background
Демонстрирует использование BackgroundWorker компонента для загрузки файла в отдельный поток. Demonstrates how to use the BackgroundWorker component to download a file on a separate thread.

Справочник Reference

BackgroundWorker
Описание класса и всех его членов. Describes this class and has links to all its members.

RunWorkerCompletedEventArgs
Описывает тип, содержащий данные для RunWorkerCompleted события. Describes the type that holds data for the RunWorkerCompleted event.

ProgressChangedEventArgs
Описывает тип, содержащий данные для ProgressChanged события. Describes the type that holds data for the ProgressChanged event.

Обзор асинхронной модели, основанной на событиях Event-based Asynchronous Pattern Overview
Описывает, как асинхронная модель делает доступными преимущества многопоточных приложений, скрывая многие из сложных проблем, присущих многопоточной архитектуре. Describes how the asynchronous pattern makes available the advantages of multithreaded applications while hiding many of the complex issues inherent in multithreaded design.

Практическое руководство. Фоновое выполнение операции How to: Run an Operation in the Background

Если какая-либо операция будет выполняться в течение долгого времени и при этом требуется не допустить задержек в работе пользовательского интерфейса, можно использовать класс BackgroundWorker для выполнения операции в другом потоке. If you have an operation that will take a long time to complete, and you do not want to cause delays in your user interface, you can use the BackgroundWorker class to run the operation on another thread.

В примере ниже показано, как запустить операцию, занимающую длительное время, в фоновом режиме. The following code example shows how to run a time-consuming operation in the background. В форме есть кнопки Пуск и Отмена. The form has Start and Cancel buttons. Кнопка Пуск служит для запуска асинхронной операции. Click the Start button to run an asynchronous operation. Кнопка Отмена служит для остановки асинхронной операции. Click the Cancel button to stop a running asynchronous operation. Результат каждой операции выводится в элементе MessageBox. The outcome of each operation is displayed in a MessageBox.

В Visual Studio предусмотрена расширенная поддержка данной задачи. There is extensive support for this task in Visual Studio.

Пример Example

Компиляция кода Compiling the Code

Для этого примера требуются: This example requires:

Оцените статью