Luxoft async.net
Upcoming SlideShare
Loading in...5
×
 

Luxoft async.net

on

  • 852 views

Презентация по асинхронному программированию в .net

Презентация по асинхронному программированию в .net

Statistics

Views

Total Views
852
Views on SlideShare
852
Embed Views
0

Actions

Likes
1
Downloads
7
Comments
0

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as Microsoft PowerPoint

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Luxoft async.net Luxoft async.net Presentation Transcript

  • Асинхронноепрограммирование в .NetСергей ТепляковSTeplyakov@luxoft.com
  • О Вашем инструкторе Сергей Тепляков Visual C# MVP, RSDN Team member Sergey.Teplyakov@gmail.com SergeyTeplyakov.blogspot.com 1-2
  • Цели курса…Слушатели изучат: Достоинства и недостатки синхронного программирования Существующие паттерны асинхронного программирования на платформе .Net Использование библиотеки PowerThreading Использование библиотеки TPL Новые возможности C# 5 1-3
  • Необходимая подготовкаСлушатели должны: Быть знакомы с основами языка C# и платформы .Net Обладать базовыми знаниями многопоточности 1-4
  • Roadmap Модель синхронного программирования Паттерны асинхронного программирования на платформе .Net Недостатки существующих моделей Библиотека PowerThreading Библиотека TPL C# 5.0: async и await 1-5
  • Недостатки синхронногопрограммирования Плохая масштабируемость Блокирование пользовательского интерфейса Низкая эффективность операций ввода/вывода Невозможность использования в некоторых контекстах (например, с JavaScript и Silverlight)
  • Плохая масштабируемость.последовательное выполнение sd Sync Client HttpWebRequest webRequest1.GetWebResponce() ProcessWebResponce1() webRequest2.GetWebResponce() ProcessWebResponce2()
  • Плохая масштабируемость Неэффективное использование мощностей современных многоядерных процессоров для CPU-Bound операций Неэффективное выполнение IO-Bound операций даже для одноядерных процессоров
  • Блокировка пользовательскогоинтерфейса
  • Блокировка пользовательскогоинтерфейса// Обработчик кнопки получения данных от веб-страницprivate void receiveDataButton_Click(object sender, EventArgs e){ Stopwatch sw = Stopwatch.StartNew(); // 1 _summaryContentLength = 0; // 2 foreach (var url in urls) { // GetResponse возвращает результат синхронно using (WebResponse webResponse = GetResponse(url)) // 3 { ProcessResponse(webResponse); // 4 executionTimeTextBox.Text = sw.ElapsedMilliseconds.ToString(); } }}
  • А ведь это и не всегдавозможно! Некоторые среды, такие как Silverlight и JavaScript не поддерживают синхронные операции
  • Рассматриваемые темы Модель синхронного программирования Паттерны асинхронного программирования на платформе .Net Недостатки существующих моделей Библиотека PowerThreading Библиотека TPL C# 5.0: async и await
  • Асинхронная модельпрограммирования Два паттерна асинхронного программирования  Classical Async Pattern  Event-Based Async Pattern
  • Classical Async Pattern Структура паттерна:// Синхронный методpublic <return> Operation(<parameters>, <out params> )// Методы классического асинхронного паттернаpublic IAsyncResult BeginOperation(<parameters>, AsyncCallback callback, object state)public <return> EndOperation(IAsyncResult asyncResult, <out paramss>) Пример:public WebResponse GetWebResponse(string url, out TimeSpan duration);public IAsyncResult BeginGetWebResponse(string ulr, object state);public WebResponse EndGetWebResponse(IAsyncResult ar, out TimeSpan duration);
  • Classical Async Pattern Метод BeginXXX инициирует асинхронную операцию Метод BeginXXX возвращает IAsyncResult, который является маркером асинхронной операции AsyncCallback представляет собой функцию, которая будет вызвана при завершении операции State представляет собой любой пользовательский объект
  • Classical Async Pattern Все by-value и by-ref параметры синхронного метода становятся by-value параметрами BeginXXX метода Тип возвращаемого значения синхронного метода совпадает с типом возвращаемого значения EndXXX метода ref и out параметры синхронного метода добавляются в EndXXX метод Исключения, генерируемые синхронным методом, генерируются методом EndXXX
  • Примеры Classical AsyncPattern Класс System.IO.Streampublic int Read(byte[] buffer, int offset, int count)public IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)public int EndRead(IAsyncResult asyncResult) Класс System.Net.WebRequestpublic WebResponse GetWebResponse();public IAsyncResult BeginGetWebResponse(AsyncCallback callback, object state);public WebResponse EndGetResponse(IAsyncResult asyncResult);
  • Асинхронное выполнениеsd ClassicAsync Form Client HttpWebRequest IO completion webRequest1.BeginGetResponse() webRequest2.BeginGetResponse() webRequest2.ProcessResponse() ProcessResponse() UpdateUIElements() webRequest1.ProcessResponse() ProcessResponse() UpdateUIElements()
  • Classical Async Pattern В обработчике UI события:  Проинициализировать дополнительные поля (теперь без них не обойтись)  Сделать неактивной кнопку  В цикле начать все операции асинхронно (вызывать метод BeginGetResponse)
  • Classical Async Pattern В обработчике завершения асинхронной операции:  Получить WebResponse (вызывать метод EndGetReponse)  Обработать ответ, не забывая, что обработка происходит не в потоке UI  Понять, что все асинхронные операции завершены (??) (посчитать количество завершенных операций)  Сделать активной кнопку, если все операции таки завершены  Если перед началом выполнения асинхронных операций были выделены ресурсы, то освободить их вручную
  • Разница синхронного иасинхронного вариантов длятрех веб-узлов Среднее выполнение синхронного варианта: 520мс Среднее выполнение асинхронного варианта: 280 мс Количество строк кода в синхронном решении: 20 Количество строк кода в асинхронном решении: 78
  • Event-Based Async Pattern
  • Event-Based Async Patternclass Class Model AsyncCompletedEv entArgs Находится в пространстве + Canceled: bool имен: System.ComponentModel Событие MethodCompleted + Error: Exception вызывается как при успешном, + UserState: object так и при неудачном завершении метода. Метод CancelMethod и событие ProgressChanged являются опциональными. MethodCompletedEv entArgs SomeType Uses + Arg1: int + event MethodCompleted + Arg2: string + event ProgressChanged + Result: MethodResult + CancelMethod() : void Uses + MethodAsync(string, int) : void MethodResult
  • Event-Based Async Pattern MethodAsync инициирует асинхронную операцию Есть только одно событие MethodCompleted, EventArgs которого содержат Result, Error, IsCanceled Возможна, но не обязательна, поддержка отмены и прогресса выполнения
  • Event-Based Async Pattern By-value и ref параметры синхронного метода являются входными параметрами метода MethodAsync Ref и out-параметры становятся readonly полями MethodCompletedEventArgs Событие MethodCompleted вызывается в «правильном» потоке (в потоке UI для WinForms или WPF)
  • Event-Based Async Pattern// Функция обработки принятых данныхprivate void receiveDataButton_Click(object sender, EventArgs e){ // Подготовка операции (отключение кнопки «Принять», // инициализация счетчиков и т.д. foreach (var url in urls) { var webClient = new WebClient(); webClient.DownloadDataAsync(new Uri(url)); webClient.DownloadDataCompleted += (s, ev) => { // Для обработки ошибки в этом случае нужно // обратиться к ствойству ev.Error. // Обработка результатов }; }}
  • Да как же между нимивыбрать? Классический паттерн более низкоуровневый и более гибкий Event-Based паттерн более простой в применении, в частности с UI дизайнерами Классический паттерн – для кода общего назначения, Event-Based – для компонентов Не применяйте оба паттерна для одного класса
  • Рассматриваемые темы Модель синхронного программирования Паттерны асинхронного программирования на платформе .Net Недостатки существующих моделей Библиотека PowerThreading Библиотека TPL C# 5.0: async и await
  • Недостатки моделейасинхронного программирования Непонятный поток исполнения (Control Flow) Сложность чтения кода и его сопровождения Сложность обработки ошибок Невозможность использования привычных языковых конструкций (using, try/finally etc)
  • Рассматриваемые темы Модель синхронного программирования Паттерны асинхронного программирования на платформе .Net Недостатки существующих моделей Библиотека PowerThreading Библиотека TPL C# 5.0: async и await
  • PowerThreading Library Разработана Джеффри Рихтер и компанией Wintellect Содержит класс AsyncEnumerator для упрощения работы с асинхронными операциями Содержит вспомогательные классы для работы с многопоточностью (ResourceLock, ReaderWriterGate etc) Другие вспомогательные классы (Disposer, Exception<T>, Singleton etc)
  • Класс AsyncEnumerator.Основные концепции Поддерживает гибкое управление асинхронными операциями (отмену, таймауты и т.п.) Использует преимущество блока итераторов (Iterator block) для упрощения потока исполнения Использует SynchronizationContext для маршалинга потока выполнения в поток UI
  • Класс AsyncEnumerator.Простой примерstatic IEnumerator<int> WorkerMethod(){ // Инициируем n асинхронных операций, // вызываем BeginRead, BeginGetWebResponse, BeginExecuteCommand etc // "возвращаем" n, что говорит AsyncEnumerator-у // о количестве запущенных асинхронных операций yield return n; // класс AsyncEnumerator вызовет в следующий раз // метод MoveNext нашего енумератора (и мы здесь получим управление) // только после завершения указанного количества асинхронных операций. // Причем, если экземпляр класса AsyncEnumerator-а создавался // с контекстом синхронизации (например, он создавался в потоке UI) // то метод MoveNext будет вызван в потоке UI! // Начинаем еще несколько асинхронных операций (например, k) yield return k;}// Создаем экземпляр енумератораAsyncEnumerator ae = new AsyncEnumerator();// Запускаем асинхронные операции, всю грязную работу// берет на себя AsyncEnumeratorae.BeginExecute(WorkerMethod(), ae.EndExecute);
  • Отступление от темы. Блокиитераторовstatic IEnumerator<int> GetNumbers(){ string padding = "tt"; Console.WriteLine(padding + "Первая строка метода GetNumbers()"); Console.WriteLine(padding + "Сразу перед yield return 7"); yield return 7; Console.WriteLine(padding + "Сразу после yield return 7"); Console.WriteLine(padding + "Сразу перед yield return 42"); yield return 42; Console.WriteLine(padding + "Сразу после yield return 42");}
  • Визуализация итераторов static IEnumerator<int> GetNumbers()WriteLine("Вызываем GetNumbers()"); {IEnumerator<int> iterator = GetNumbers(); string padding = "tt"; Console.WriteLine(padding + "Первая строка метода GetNumbers()"); // 1WriteLine("Вызываем MoveNext()..."); Console.WriteLine(padding + "Сразу перед yield return 7"); // 2bool more = iterator.MoveNext(); yield return 7; // 3WriteLine("Result=...", iterator.Current); Console.WriteLine(padding + "Сразу после yield return 7"); // 4 Console.WriteLine(padding + "Сразу перед yield return 42"); // 5WriteLine("Снова вызываем MoveNext()..."); yield return 42; // 6more = iterator.MoveNext(); Console.WriteLine(padding + "Сразу после yield return 42"); //7WriteLine("Result=...", iterator.Current); }WriteLine("Снова вызываем MoveNext()...");more = iterator.MoveNext();WriteLine("Result={0} (stopping)", more);Результат:Вызываем GetNumbers()
  • Визуализация итераторов static IEnumerator<int> GetNumbers()WriteLine("Вызываем GetNumbers()"); {IEnumerator<int> iterator = GetNumbers(); string padding = "tt"; Console.WriteLine(padding + "Первая строка метода GetNumbers()");WriteLine("Вызываем MoveNext()..."); Console.WriteLine(padding + "Сразу перед yield return 7");bool more = iterator.MoveNext(); yield return 7;WriteLine("Result=...", iterator.Current); Console.WriteLine(padding + "Сразу после yield return 7"); Console.WriteLine(padding + "Сразу перед yield return 42");WriteLine("Снова вызываем MoveNext()..."); yield return 42; // 6more = iterator.MoveNext(); Console.WriteLine(padding + "Сразу после yield return 42");WriteLine("Result=...", iterator.Current); }WriteLine("Снова вызываем MoveNext()...");more = iterator.MoveNext();WriteLine("Result={0} (stopping)", more);Результат:Вызываем GetNumbers()Вызываем MoveNext()... Первая строка метода GetNumbers() Сразу перед yield return 7Result=True; Current=7
  • Визуализация итераторов static IEnumerator<int> GetNumbers()WriteLine("Вызываем GetNumbers()"); {IEnumerator<int> iterator = GetNumbers(); string padding = "tt"; Console.WriteLine(padding + "Первая строка метода GetNumbers()");WriteLine("Вызываем MoveNext()..."); Console.WriteLine(padding + "Сразу перед yield return 7");bool more = iterator.MoveNext(); yield return 7;WriteLine("Result=...", iterator.Current); Console.WriteLine(padding + "Сразу после yield return 7"); Console.WriteLine(padding + "Сразу перед yield return 42");WriteLine("Снова вызываем MoveNext()..."); yield return 42;more = iterator.MoveNext(); Console.WriteLine(padding + "Сразу после yield return 42");WriteLine("Result=...“, iterator.Current); }WriteLine("Снова вызываем MoveNext()...");more = iterator.MoveNext();WriteLine("Result={0} (stopping)", more);Результат:Вызываем GetNumbers()Вызываем MoveNext()... Первая строка метода GetNumbers() Сразу перед yield return 7Result=True; Current=7Снова вызываем MoveNext()... Сразу после yield return 7 Сразу перед yield return 42Result=True; Current=42
  • Визуализация итераторов static IEnumerator<int> GetNumbers()WriteLine("Вызываем GetNumbers()"); {IEnumerator<int> iterator = GetNumbers(); string padding = "tt"; Console.WriteLine(padding + "Первая строка метода GetNumbers()");WriteLine("Вызываем MoveNext()..."); Console.WriteLine(padding + "Сразу перед yield return 7");bool more = iterator.MoveNext(); yield return 7;WriteLine("Result=...", iterator.Current); Console.WriteLine(padding + "Сразу после yield return 7"); Console.WriteLine(padding + "Сразу перед yield return 42");WriteLine("Снова вызываем MoveNext()..."); yield return 42;more = iterator.MoveNext(); Console.WriteLine(padding + "Сразу после yield return 42");WriteLine("Result=...", iterator.Current); }WriteLine("Снова вызываем MoveNext()...");more = iterator.MoveNext();WriteLine("Result={0} (stopping)", more);Результат:Вызываем GetNumbers()Вызываем MoveNext()... Первая строка метода GetNumbers() Сразу перед yield return 7Result=True; Current=7Снова вызываем MoveNext()... Сразу после yield return 7 Сразу перед yield return 42Result=True; Current=42Result=False (stopping)
  • AsyncEnumerator. Участники Класс AsyncEnumerator Рабочий метод, возвращающий IEnumerator<int> Кто-то, связывающий все это воедино
  • AsyncEnumeratorsd AsyncEnumerator AsyncEnumerator WorkerMethod WebRequest IO Completion calling to MoveNext() webRequest1.BeginGetResponse() Первый вызов MoveNext енумератора инициирует ас инхронные операции. webRequest2.BeginGetResponse() WebRequest2Complete() WebRequest1Complete() calling to MoveNext() Т еперь рабочий метод В качес тве метода обработки (WorkerMethod) может обработать завершения ас инхронной завершенные операции и начать операции передаетс я метод новые ас инхронные операции. клас с а AsyncEnumerator-а.
  • Пример использования классаAsyncEnumeratorprivate IEnumerator<int> GetWebData(AsyncEnumerator enumerator){ // Начинаем несколько асинхронных операций WebRequest webRequest1 = WebRequest.Create(url1); webRequest1.BeginGetResponse(enumerator.End(), null); WebRequest webRequest2 = WebRequest.Create(url2); webRequest2.BeginGetResponse(enumerator.End(), null); yield return 2; // 2 - это количество асинхронных операций // Сюда мы попадем уже тогда, когда все асинхронные операции завершены WebResponse webResponse1 = webRequest1.EndGetResponse(enumerator.DequeueAsyncResult()); WebResponse webResponse2 = webRequest2.EndGetResponse(enumerator.DequeueAsyncResult()); // Обрабатываем полученные результаты аналогичным образом}
  • Пример использования классаAsyncEnumeratorprivate void receiveDataButton_Click(object sender, EventArgs e){ asyncEnumerator = new AsyncEnumerator(); // AsyncEnumerator автоматически запоминает контекст синхронизации // Запускаем процесс получения данных асинхронно asyncEnumerator.BeginExecute(GetWebData(asyncEnumerator), asyncEnumerator.EndExecute);}
  • Рассматриваемые темы Модель синхронного программирования Паттерны асинхронного программирования на платформе .Net Недостатки существующих моделей Библиотека PowerThreading Библиотека TPL C# 5.0: async и await
  • Библиотека TPL Параллелизм задач Класс Parallel (Parallel.ForEach etc) Parallel LINQ (a.k.a. PLINQ)
  • Ключевые классы Task – инкапсулирует единицу работы Task<TResult> - единица работы с определенным типом результата TaskFactory, TaskFactory<TResult> – фабрики создания задач TaskScheduler – управляет «расписанием» запуска задач TaskCompletionSource – управляет «временем жизни» задачи
  • Класс Task<T> Представляет собой «незавершенную операцию» или «единицу работы» Это может быть операция ввода/вывода, операция в фоновом потоке, в выделенном потоке и т.д. Поддерживает «продолжения» с помощью ContinueWithvar task2 = task1.ContinueWith(t => … t.Result …); Содержит ряд вспомогательных методов: WhenAll, WhenAny etc
  • Примеры использования1. Простая задачаTask.Factory.StartNew(() => Console.WriteLine("Hello from a task!"));2. Запуск задачи в выделенном потокеTask task = Task.Factory.StartNew( () => { Console.WriteLine("Выполняем длительную операцию"); Thread.Sleep(TimeSpan.FromHours(1)); Console.WriteLine("Длительная операция завершена"); }, TaskCreationOptions.LongRunning);
  • Примеры использования3. Получение данных от веб-узлаTask<long> task = new Task<long>(() => { var webRequest = WebRequest.Create(url); using (var webResponse = webRequest.GetResponse()) { return webResponse.ContentLength; } });task.Start();task.Wait();long result = task.Result;4. Использование классического асинхронного APIvar webRequest = WebRequest.Create(url);Task<WebResponse> task = Task<WebResponse>.Factory.FromAsync( webRequest.BeginGetResponse, webRequest.EndGetResponse, null);task.Wait();var response = task.Result;
  • ПродолженияTask task1 = new Task(() => Console.WriteLine("Task1..."));Task task2 = task1.ContinueWith(t1 => Console.WriteLine("tTask2..."));Task task3 = task2.ContinueWith(t2 => Console.WriteLine("ttTask3..."));// Запускаем цепочку задачConsole.WriteLine("Запускаем первую задачу");task1.Start();Console.WriteLine("Ожидаем завершения цепочки задач");task3.Wait();Console.WriteLine("Все задачи завершены");
  • ПродолженияTask task1 = new Task(() => Console.WriteLine("Task1..."));Task task2 = task1.ContinueWith(t1 => Console.WriteLine("tTask2..."));Task task3 = task2.ContinueWith(t2 => Console.WriteLine("ttTask3..."));// Запускаем цепочку задачConsole.WriteLine("Запускаем первую задачу");task1.Start();Console.WriteLine("Ожидаем завершения цепочки задач");task3.Wait();Console.WriteLine("Все задачи завершены"); Вывод: (ничего не происходит, поскольку задача еще не запущена)
  • ПродолженияTask task1 = new Task(() => Console.WriteLine("Task1..."));Task task2 = task1.ContinueWith(t1 => Console.WriteLine("tTask2..."));Task task3 = task2.ContinueWith(t2 => Console.WriteLine("ttTask3..."));// Запускаем цепочку задачConsole.WriteLine("Запускаем первую задачу");task1.Start();Console.WriteLine("Ожидаем завершения цепочки задач");task3.Wait();Console.WriteLine("Все задачи завершены"); Вывод: (все еще ничего не происходит, поскольку первая задача не запущена) (первой задаче установлена вторая задача в виде продолжения)
  • ПродолженияTask task1 = new Task(() => Console.WriteLine("Task1..."));Task task2 = task1.ContinueWith(t1 => Console.WriteLine("tTask2..."));Task task3 = task2.ContinueWith(t2 => Console.WriteLine("ttTask3..."));// Запускаем цепочку задачConsole.WriteLine("Запускаем первую задачу");task1.Start();Console.WriteLine("Ожидаем завершения цепочки задач");task3.Wait();Console.WriteLine("Все задачи завершены"); Вывод: (все еще ничего не происходит, поскольку первая задача не запущена) (Связке первых двух задач установлена третья задача в виде продолжения)
  • ПродолженияTask task1 = new Task(() => Console.WriteLine("Task1..."));Task task2 = task1.ContinueWith(t1 => Console.WriteLine("tTask2..."));Task task3 = task2.ContinueWith(t2 => Console.WriteLine("ttTask3..."));// Запускаем цепочку задачConsole.WriteLine("Запускаем первую задачу");task1.Start();Console.WriteLine("Ожидаем завершения цепочки задач");task3.Wait();Console.WriteLine("Все задачи завершены"); Вывод: Запускаем первую задачу
  • ПродолженияTask task1 = new Task(() => Console.WriteLine("Task1..."));Task task2 = task1.ContinueWith(t1 => Console.WriteLine("tTask2..."));Task task3 = task2.ContinueWith(t2 => Console.WriteLine("ttTask3..."));// Запускаем цепочку задачConsole.WriteLine("Запускаем первую задачу");task1.Start();Console.WriteLine("Ожидаем завершения цепочки задач");task3.Wait();Console.WriteLine("Все задачи завершены"); Вывод: Запускаем первую задачу Ожидаем завершения цепочки задач
  • ПродолженияTask task1 = new Task(() => Console.WriteLine("Task1..."));Task task2 = task1.ContinueWith(t1 => Console.WriteLine("tTask2..."));Task task3 = task2.ContinueWith(t2 => Console.WriteLine("ttTask3..."));// Запускаем цепочку задачConsole.WriteLine("Запускаем первую задачу");task1.Start();Console.WriteLine("Ожидаем завершения цепочки задач");task3.Wait();Console.WriteLine("Все задачи завершены"); Вывод: (Выполняется первая задача) Запускаем первую задачу Ожидаем завершения цепочки задач Task1...
  • ПродолженияTask task1 = new Task(() => Console.WriteLine("Task1..."));Task task2 = task1.ContinueWith(t1 => Console.WriteLine("tTask2..."));Task task3 = task2.ContinueWith(t2 => Console.WriteLine("ttTask3..."));// Запускаем цепочку задачConsole.WriteLine("Запускаем первую задачу");task1.Start();Console.WriteLine("Ожидаем завершения цепочки задач");task3.Wait();Console.WriteLine("Все задачи завершены"); Вывод: (Выполняется вторая задача, поскольку первая уже завершилась) Запускаем первую задачу Ожидаем завершения цепочки задач Task1... Task2...
  • ПродолженияTask task1 = new Task(() => Console.WriteLine("Task1..."));Task task2 = task1.ContinueWith(t1 => Console.WriteLine("tTask2..."));Task task3 = task2.ContinueWith(t2 => Console.WriteLine("ttTask3..."));// Запускаем цепочку задачConsole.WriteLine("Запускаем первую задачу");task1.Start();Console.WriteLine("Ожидаем завершения цепочки задач");task3.Wait();Console.WriteLine("Все задачи завершены"); Вывод: (Выполняется третья задача, поскольку первая и вторая уже завершились) Запускаем первую задачу Ожидаем завершения цепочки задач Task1... Task2... Task3...
  • ПродолженияTask task1 = new Task(() => Console.WriteLine("Task1..."));Task task2 = task1.ContinueWith(t1 => Console.WriteLine("tTask2..."));Task task3 = task2.ContinueWith(t2 => Console.WriteLine("ttTask3..."));// Запускаем цепочку задачConsole.WriteLine("Запускаем первую задачу");task1.Start();Console.WriteLine("Ожидаем завершения цепочки задач");task3.Wait();Console.WriteLine("Все задачи завершены"); Вывод: (Выполняется третья задача, поскольку первая и вторая уже завершились) Запускаем первую задачу Ожидаем завершения цепочки задач Task1... Task2... Task3... Все задачи завершены
  • Рассматриваемые темы Модель синхронного программирования Паттерны асинхронного программирования на платформе .Net Недостатки существующих моделей Библиотека PowerThreading Библиотека TPL C# 5.0: async и await
  • Модель асинхронногопрограммирования в C# 5 Аналогична синхронной модели Построена по тому же принципу, что и класс AsyncEnumerator Использует Task<T> для асинхронных операций Никто не знает, когда выйдет C# 5 Сейчас доступно Async CTP (Community Technology Preview)
  • Модель асинхронногопрограммирования в C# 5 Два новых ключевых слова:  async – указывает, что метод или лямбда- выражение является асинхронным  await – является аналогом yield return и возвращает сразу же управление вызывающему коду до тех пор, пока асинхронная операция не будет завершена
  • Модель асинхронногопрограммирования в C# 5static async Task<long> GetWebResponseContentLength(string url){ var webRequest = WebRequest.Create(url); Console.WriteLine("Перед вызовом await-a. Thread Id: {0}", Thread.CurrentThread.ManagedThreadId); // Начинаем асинхронную операцию Task<WebResponse> responseTask = webRequest.GetResponseAsync(); // Ожидаем получения ответа WebResponse webResponse = await responseTask; Console.WriteLine("После завершения await-а. Thread Id: {0}", Thread.CurrentThread.ManagedThreadId); // В этой строке мы уже получили ответ от веб-узла // можем обрабатывать результаты. Тип возвращаемого значения // должен соответствовать обобщенному параметру класса Task return webResponse.ContentLength;}
  • Работа простого методаWriteLine("Начало исполнения. Thread Id: {0}"); static async Task<long> GetWebResponseContentLength(string url) {Task<long> task = var webRequest = WebRequest.Create(url); GetWebResponseContentLength(url); WriteLine("Перед вызовом await-a. Thread Id: {0}");// ожидаем завершения асинхронной операции // Начинаем асинхронную операциюtask.Wait(); Task<WebResponse> respTsk = webRequest.GetResponseAsync();WriteLine("ContentLength: {0}, Thread Id: {1}"); // Ожидаем получения ответа WebResponse webResponse = await respTsk; WriteLine("После завершения await-а. Thread Id: {0}"); Результаты: Начало исполнения. Thread Id: 10 return webResponse.ContentLength; } Перед вызовом await-a. Thread Id: 10 (Асинхронная операция запущена)
  • Работа простого методаWriteLine("Начало исполнения. Thread Id: {0}"); static async Task<long> GetWebResponseContentLength(string url) {Task<long> task = var webRequest = WebRequest.Create(url); GetWebResponseContentLength(url); WriteLine("Перед вызовом await-a. Thread Id: {0}");// ожидаем завершения асинхронной операции // Начинаем асинхронную операциюtask.Wait(); Task<WebResponse> respTsk = webRequest.GetResponseAsync();WriteLine("ContentLength: {0}, Thread Id: {1}"); // Ожидаем получения ответа WebResponse webResponse = await respTsk; WriteLine("После завершения await-а. Thread Id: {0}"); Результаты: return webResponse.ContentLength; Начало исполнения. Thread Id: 10 } Перед вызовом await-a. Thread Id: 10 После завершения await-a. Thread Id: 14 (Эта строка выполнится только после завершения операции)
  • Работа простого методаWriteLine("Начало исполнения. Thread Id: {0}"); static async Task<long> GetWebResponseContentLength(string url) {Task<long> task = var webRequest = WebRequest.Create(url); GetWebResponseContentLength(url); WriteLine("Перед вызовом await-a. Thread Id: {0}");// ожидаем завершения асинхронной операции // Начинаем асинхронную операциюtask.Wait(); Task<WebResponse> respTsk = webRequest.GetResponseAsync();WriteLine("ContentLength:{0},Thread Id: {1}"); // Ожидаем получения ответа WebResponse webResponse = await respTsk; WriteLine("После завершения await-а. Thread Id: {0}"); Результаты: return webResponse.ContentLength; } Начало исполнения. Thread Id: 10 Перед вызовом await-a. Thread Id: 10 После завершения await-a. Thread Id: 14 ContentLength: 1672, Thread Id: 10
  • Сравнение с AsyncEnumerator IEnumerator<T> -> async Task<T> yield return n -> await task
  • Модель асинхронногопрограммирования в C# 5 Асинхронный метод может возвращать  void – для асинхронных операций типа “fire and forget”  Task – вызывающий код может дождаться завершения асинхронной операции, которая не возвращает значения  Task<T> - для асинхронной операции, возвращающей T (string для Task<string> etc)
  • Модель асинхронногопрограммирования C# 5private async void receiveDataButton_Click(object sender, EventArgs e){ Stopwatch sw = Stopwatch.StartNew(); receiveDataButton.Enabled = false; IEnumerable<Task<WebResponse>> tasks = from url in urls let webRequest = WebRequest.Create(url) select webRequest.GetResponseAsync(); // Начинаем выполнять все задачи WebResponse[] webResponses = await TaskEx.WhenAll(tasks); // Теперь мы можем обработать результаты long summaryContentLength = webResponses.Sum(s => s.ContentLength); executionTimeTextBox.Text = sw.ElapsedMilliseconds.ToString(); summaryContentLengthTextBox.Text = summaryContentLength.ToString(); receiveDataButton.Enabled = true; foreach(var wr in webResponses) wr.Close();}
  • Преимущества новойасинхронной модели Простота использования Привычный поток исполнения Простота обработки ошибок и возможность использования конструкций using, try/finally etctry { WebResponse[] data = await TaskEx.WhenAll(tasks); // Обработка данных}catch (WebException we) { //Обработка ошибки получения данных}
  • Преимущества новойасинхронной модели Построена на основе проверенных идиом (Iterator blocks, AsyncEnumerator, Reactive Extensions) Построена на основе TPL (преимущества от ее использования можно закладывать уже сейчас)
  • Что мы изучили? Модель синхронного программирования Паттерны асинхронного программирования на платформе .Net Недостатки существующих моделей Библиотека PowerThreading Библиотека TPL C# 5.0: async и await
  • Дополнительные ссылки Visual Studio Asynchronous Programming (http://msdn.microsoft.com/en- us/vstudio/async.aspx) Асинхронные операции и AsyncEnumerator (http://sergeyteplyakov.blogspot.com/2010/10/asyncenumerator.html) «Реактивные расширения и асинхронные операции (http://sergeyteplyakov.blogspot.com/2010/11/blog-post.html) Знакомство с асинхронными операциями в C# 5 (http://sergeyteplyakov.blogspot.com/2010/12/c-5.html) Джефри Рихтер. Упрощение модели асинхронного программирования с помощью AsyncEnumerator (http://msdn.microsoft.com/ru- ru/magazine/cc546608.aspx) Джеффри Рихтер. Дополнительные возможности AsyncEnumerator (http://msdn.microsoft.com/ru-ru/magazine/cc721613.aspx)
  • Дополнительные ссылки Итераторы в языке программирования C#  Часть 1: http://sergeyteplyakov.blogspot.com/2010/06/c-1.html  Часть 2: http://sergeyteplyakov.blogspot.com/2010/06/c-2.html  Часть 3: http://sergeyteplyakov.blogspot.com/2010/06/c-3.html Eric Lippert. Continuation Passing Style: http://blogs.msdn.com/b/ericlippert/archive/tags/continuation+passing+styl e/ Reactive Extensions Official release (http://channel9.msdn.com/Blogs/Charles/Announcing-the-Official-Release- of-Rx)
  • Вопросы?