Опубликован: 02.08.2013 | Доступ: свободный | Студентов: 468 / 15 | Длительность: 18:38:00
Специальности: Программист
Самостоятельная работа 14:

Многопоточное программирование

В коде файла 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.

Выводы

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

Задание

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

Дополнительные материалы

К данной лекции подготовлено видеоприложение и демонстрационный программный проект.