Многопоточное программирование
В коде файла 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.
Выводы
В этой лабораторной работе мы рассмотрели особенности работы с асинхронным кодом. Использование такого кода позволяет при выполнении ресурсоёмких операция переложить вычислительную нагрузку на потоки, отличные от потока пользовательского интерфейса. В итоге, пользовательский интерфейс не блокируется, пользователь может взаимодействовать с приложением во время работы асинхронного кода.
Задание
Проанализируйте код приложения, которое вы разрабатываете, испытайте различные варианты работы с ним и рассмотрите возможность асинхронного исполнения участков кода, которые выполняют в потоке пользовательского интерфейса какие-либо ресурсоёмкие операции (например, обрабатывают большие массивы данных). Подготовьте отчёт, содержащий данные анализа кода приложения на предмет асинхронного выполнения, и, если это применимо к вашему приложению, примеры модифицированного кода.
Дополнительные материалы
К данной лекции подготовлено видеоприложение и демонстрационный программный проект.