(495) 925-0049, ITShop интернет-магазин 229-0436, Учебный Центр 925-0049
  Главная страница Карта сайта Контакты
Поиск
Вход
Регистрация
Рассылки сайта
 
 
 
 
 

C# 5 - об async/await с начала

В недавно вышедшей Visual Studio 11 Beta встроена новая и главная фишка будущего C# 5 - асинхронное программирование с помощью async/await. Про нее уже написано достаточно много статей в том, числе на хабре - например, эта серия статей. Однако, я для себя так и не понял в чем суть нового синтаксиса, пока сам не попробовал его в деле. Данная статья - попытка самому структурировать и до конца разобраться с этим достаточно интересным инструментом и поделиться результатами с сообществом, рассказав про него немного иначе. Итак, поехали…

Зачем это нужно?

 C# активно развивающийся язык в каждой версии которого появляются интересные особенности, которые действительно упрощают написание качественного и понятного кода. Например, Linq значительно упростил написание фильтров для коллекций и sql-запросов в коде. Сейчас дошла очередь до асинхронности.

 И хотя во фреймворке уже накопилось достаточно большое количество способов написания многопоточного кода, от ручного создания потока до визуального компонента BackgroundWorker, разработка асинхронного приложения требует написания приличного объема инфраструктурного кода, который увеличивается по мере усложнения задачи. Главной проблемой этого, на мой взгляд, является необходимость изменения состояния пользовательского интерфейса из фонового потока (чего, как известно, нельзя делать напрямую).

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

Встречаем: async/await

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

// Синхронная версия
private void OnButtonClick()
{
   TextBox.Text = new WebClient().DownloadString("http://habrahabr.ru/"); 
}

// Асинхронная версия
private async void OnButtonClick()
{
   TextBox.Text = await new WebClient().DownloadStringTaskAsync("http://habrahabr.ru/");
}

 И если в синхронном варианте все просто и понятно, то с асинхронным возникает много вопросов. Начнем как ни странно с нового метода у класса WebClient - DownloadStringTaskAsync - этот метод возвращает Task<string> и в новой студии отмечается как awaitable. Тип возвращаемого значения ключевой момент во всей этой истории - забегая вперед стоит сказать, что await умеет работать только с функциями возвращающими Task и Task<T>.
UPD: как справедливо заметил jack128 в комментариях, await работает с любым объектом, у которого есть метод GetAwaiter(), спасибо ему за корректировку.

 Итак, метод DownloadStringTaskAsync создает задачу Task и сразу возвращает ее из функции, в то время как в фоновом потоке начинает скачиваться страница с запрошенного url. Мы вольны работать непосредственно с объектом Task вручную дождавшись выполнения результата:

private void OnButtonClick()
{
  Task<string> task = new WebClient().DownloadStringTaskAsync("http://microsoft.com/");
  task.Wait(); // Здесь мы ждем завершения задачи, что блокирует поток
  TextBox.Text = task.Result;
}

 Данный код разумеется остается синхронным, так как мы в основном потоке ждем выполнения фонового…

 Нужен способ как удобно и асинхронно работать с задачей (Task<T>), которая осталась единственной "ниточкой", которая связывает нас с фоновым потоком. И здесь на сцене появляется await - он не только разворачивает Task<T> в T, но и устанавливает остаток метода в "продолжение" (continuation), которое выполнится после завершения задачи и самое главное в том же потоке. При этом произойдет выход из функции OnButtonClick() и приложение продолжит работать в штатном режиме - реагируя на действия пользователей.

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

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

Как это выглядит в действии

 В функции может быть несколько await'ов, что позволяет создавать асинхронные цепочки выполнения:

private async void StartButtonClick(object sender, RoutedEventArgs e)
{
   // Убираем возможность повторного нажатия на кнопку
   StartButton.IsEnabled = false;

   // Вызываем новую задачу, на этом выполнение функции закончится
   // а остаток функции установится в продолжение
   TextBox.Text = await new WebClient().DownloadStringTaskAsync("http://habrahabr.ru/");
   StatusLabel.Content = "Загрузка страницы завершена, начинается обработка";

   // В продолжении можно также запускать асинхронные операции со своим продолжением
   var result = await Task<string>.Factory.StartNew(() =>
   {       
     Thread.Sleep(5000); // Имитация длительной обработки...
     return "Результат обработки";
   });

   // Продолжение второй асинхронной операции
   StatusLabel.Content = result;
   StartButton.IsEnabled = true;
}

 А что на счет исключений? Раз уж разработчики пообещали, что новый синтаксис будет простым и близким к синхронному, они не могли обойти стороной такую важную проблему обработки исключений. Как и обещалось, исключения брошенные в фоновом потоке, можно обработать используя классический синтаксис (как будто никакой асинхронности и нет):

private async void StartButtonClick(object sender, RoutedEventArgs e)
{
   try
   {
     TextBox.Text = await new WebClient().DownloadStringTaskAsync("http://not-habrahabr.ru/");
   }
   catch (Exception ex)
   {
     MessageBox.Show(ex.Message);
   }
}

 Однако, с обработкой исключений есть один момент, который нужно понимать - так как весь код, идущий после await устанавливается в завершение и когда он будет выполнен вообще неизвестно, то такая обработка исключений не будет работать:

// Это не работает!!!
private void StartButtonClick(object sender, RoutedEventArgs e)
{
   try
   {    
     Download();    
   }
   catch (Exception ex)
   {
     MessageBox.Show(ex.Message);
   }
}

private async void Download()
{
   TextBox.Text = await new WebClient().DownloadStringTaskAsync("http://not-habrahabr.ru/");
}

 Функция Download вернет управление как только будет создан Task с фоновым потоком, а после этого будет выполнен и выход из функции StartButtonClick… и уже позже в фоновом потоке будет сгенерировано исключение о том, что не удается разрешить доменное имя. Более подробное объяснение можно почитать здесь.

Итого

 В грядущем .Net 4.5 многие классы, будет дополнены для поддержки нового синтаксиса - т.е. появится много функций возвращающих Task и Task<T>. И судя по простоте нового синтаксиса он получит большое распространение, поэтому необходимо ясное понимание новых конструкций языка, их действия и области применения.

 Подытожим - ключевое слово async не приводит к тому, что метод будет выполняться в фоновом потоке (как кажется из названия), а только отмечает, что внутри метода присутствует await, который работает с Task и Task<T> таким образом, что остаток метода после await будет выполнен после завершения Task, но в основном потоке.

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

Ссылки по теме


 Распечатать »
 Правила публикации »
  Написать редактору 
 Рекомендовать » Дата публикации: 15.03.2012 
 

Магазин программного обеспечения   WWW.ITSHOP.RU
Microsoft 365 Business Basic (corporate)
Microsoft Windows Professional 10, Электронный ключ
Microsoft Office 365 для Дома 32-bit/x64. 5 ПК/Mac + 5 Планшетов + 5 Телефонов. Подписка на 1 год.
Microsoft Office 365 Персональный 32-bit/x64. 1 ПК/MAC + 1 Планшет + 1 Телефон. Все языки. Подписка на 1 год.
Microsoft 365 Apps for business (corporate)
 
Другие предложения...
 
Курсы обучения   WWW.ITSHOP.RU
 
Другие предложения...
 
Магазин сертификационных экзаменов   WWW.ITSHOP.RU
 
Другие предложения...
 
3D Принтеры | 3D Печать   WWW.ITSHOP.RU
 
Другие предложения...
 
Новости по теме
 
Рассылки Subscribe.ru
Информационные технологии: CASE, RAD, ERP, OLAP
Безопасность компьютерных сетей и защита информации
Новости ITShop.ru - ПО, книги, документация, курсы обучения
Программирование на Microsoft Access
CASE-технологии
Программирование на Visual С++
Новые программы для Windows
 
Статьи по теме
 
Новинки каталога Download
 
Исходники
 
Документация
 
 



    
rambler's top100 Rambler's Top100