Многопоточное программирование
В коде файла MainPage.xaml.cs (Листинг 2) показана работа синхронных механизмов (обработчик нажатия на кнопку btnSyncWork и асинхронных (их запуск производится при нажатии на кнопку btnWork.
using System; using System.Windows; using Microsoft.Phone.Controls; using Microsoft.Phone.Shell; using System.ComponentModel; using System.Threading; namespace CSWP8ProgressIndicator { public partial class MainPage : PhoneApplicationPage { static string strMsg = string.Empty; // Сообщение о результате. // Конструктор public MainPage() { InitializeComponent(); } private void btnWork_Click(object sender, RoutedEventArgs e) { //Добавлено, воспроизведение анимации Storyboard1.Begin(); // Создание и настройка нового индикатора выполнения ProgressIndicator prog = new ProgressIndicator(); prog.IsVisible = true; prog.IsIndeterminate = true; prog.Text = "Выполнение задачи..."; //Установка нового индикатора SystemTray.SetProgressIndicator(this, prog); Deployment.Current.Dispatcher.BeginInvoke(() => { btnWork.Visibility = Visibility.Collapsed; tbMessage.Text = "Рабочий процесс запущен, пожалуйста, подождите 5 секунд."; }); RunProcessAsync(DateTime.Now); } /// <summary> /// Запуск процесса /// </summary> /// <param name="dumpDate"></param> public void RunProcessAsync(DateTime dumpDate) { //Новый фоновый рабочий процесс BackgroundWorker worker = new BackgroundWorker(); //Подписка на событие завершения работы worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted); worker.DoWork += new DoWorkEventHandler(worker_DoWork); worker.RunWorkerAsync(dumpDate); } /// <summary> /// Обработчик события DoWork /// </summary> /// <param name="sender"></param> /// <param name="e"></param> async void worker_DoWork(object sender, DoWorkEventArgs e) { BackgroundWorker worker = new BackgroundWorker(); // Здесь следует разместить код, выполняющий ресурсоёмкую задачу Thread.Sleep(5 * 1000); // 5 секунд; await worker.RunWorkerTaskAsync(); } //Обработчик события завершения работы фонового процесса void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { BackgroundWorker worker = sender as BackgroundWorker; worker.RunWorkerCompleted -= new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted); worker.DoWork -= new DoWorkEventHandler(worker_DoWork); Deployment.Current.Dispatcher.BeginInvoke(() => { tbMessage.Text = "Выполнение задачи завершено."; btnWork.Visibility = Visibility.Visible; }); SystemTray.ProgressIndicator.IsVisible = false; } //Добавлено, иллюстрация синхронного кода private void btnSyncWork_Click(object sender, RoutedEventArgs e) { Storyboard1.Begin(); tbMessage.Text = "Выполнение задачи начато, пожалуйста, подождите 5 секунд."; Thread.Sleep(5 * 1000); tbMessage.Text = "Выполнение задачи завершено."; } } }Листинг 43.2. Код файла MainPage.xaml.cs
Обратите внимание на место в методе worker_DoWork, где следует разместить ресурсоёмкий код. Здесь размещена команда Thread.Sleep(5*1000), которая позволяет заблокировать текущий поток на заданное количество миллисекунд, 5000 миллисекунд – это 5 секунд.
Для класса BackgroundWorker в рассматриваемом проекте определен метод расширения RunWorkerTaskAsync. Такие методы определяются как статические, при их определении указывается тип, с которым оперирует метод, вызов метода осуществляется для экземпляра класса – именно это происходит в вызове await worker.RunWorkerTaskAsync();. Код метода расположен в файле BackgroundWorkerExtensions, Листинг 43.3.
using System.ComponentModel; using System.Threading.Tasks; namespace CSWP8ProgressIndicator { //Задача для фонового процесса public static class BackgroundWorkerExtensions { public static Task<object> RunWorkerTaskAsync(this BackgroundWorker backgroundWorker) { var tcs = new TaskCompletionSource<object>(); RunWorkerCompletedEventHandler handler = null; handler = (sender, args) => { if (args.Cancelled) tcs.TrySetCanceled(); else if (args.Error != null) tcs.TrySetException(args.Error); else tcs.TrySetResult(args.Result); }; backgroundWorker.RunWorkerCompleted += handler; try { backgroundWorker.RunWorkerAsync(); } catch { backgroundWorker.RunWorkerCompleted -= handler; throw; } return tcs.Task; } } }Листинг 43.3. Код файла BackgroundWorkerExtensions.CS
Для получения дополнительных сведений по использованию асинхронных методов и работе с потоками рекомендуется обратиться к материалам "Асинхронное программирование с использованием ключевых слов Async и Await" и "Работа с потоками" раздела "Основные понятия программирования", http://msdn.microsoft.com/ru-ru/library/dd460655.aspx, который посвящен особенностям работы в Visual Studio 2012.
Выводы
В этой лабораторной работе мы рассмотрели особенности работы с асинхронным кодом. Использование такого кода позволяет при выполнении ресурсоёмких операция переложить вычислительную нагрузку на потоки, отличные от потока пользовательского интерфейса. В итоге, пользовательский интерфейс не блокируется, пользователь может взаимодействовать с приложением во время работы асинхронного кода.
Задание
Проанализируйте код приложения, которое вы разрабатываете, испытайте различные варианты работы с ним и рассмотрите возможность асинхронного исполнения участков кода, которые выполняют в потоке пользовательского интерфейса какие-либо ресурсоёмкие операции (например, обрабатывают большие массивы данных). Подготовьте отчёт, содержащий данные анализа кода приложения на предмет асинхронного выполнения, и, если это применимо к вашему приложению, примеры модифицированного кода.
Дополнительные материалы
К данной лекции подготовлено видеоприложение и демонстрационный программный проект.