Async

1,624 views
1,540 views

Published on

Published in: Technology
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,624
On SlideShare
0
From Embeds
0
Number of Embeds
653
Actions
Shares
0
Downloads
6
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Async

  1. 1. Асинхронноепрограммирование в .NetСергей ТепляковSTeplyakov@luxoft.com
  2. 2. О Вашем инструкторе Сергей Тепляков Visual C# MVP, RSDN Team member Sergey.Teplyakov@gmail.com SergeyTeplyakov.blogspot.com 1-2
  3. 3. Цели курса…Слушатели изучат: Достоинства и недостатки синхронного программирования Существующие паттерны асинхронного программирования на платформе .Net Использование библиотеки PowerThreading Использование библиотеки TPL Новые возможности C# 5 1-3
  4. 4. Необходимая подготовкаСлушатели должны: Быть знакомы с основами языка C# и платформы .Net Обладать базовыми знаниями многопоточности 1-4
  5. 5. Roadmap Модель синхронного программирования Паттерны асинхронного программирования на платформе .Net Недостатки существующих моделей Библиотека PowerThreading Библиотека TPL C# 5.0: async и await 1-5
  6. 6. Недостатки синхронногопрограммирования Плохая масштабируемость Блокирование пользовательского интерфейса Низкая эффективность операций ввода/вывода Невозможность использования в некоторых контекстах (например, с JavaScript и Silverlight)
  7. 7. Плохая масштабируемость.последовательное выполнение sd Sync Client HttpWebRequest webRequest1.GetWebResponce() ProcessWebResponce1() webRequest2.GetWebResponce() ProcessWebResponce2()
  8. 8. Плохая масштабируемость Неэффективное использование мощностей современных многоядерных процессоров для CPU-Bound операций Неэффективное выполнение IO-Bound операций даже для одноядерных процессоров
  9. 9. Блокировка пользовательскогоинтерфейса
  10. 10. Блокировка пользовательскогоинтерфейса// Обработчик кнопки получения данных от веб-страниц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(); } }}
  11. 11. А ведь это и не всегдавозможно! Некоторые среды, такие как Silverlight и JavaScript не поддерживают синхронные операции
  12. 12. Рассматриваемые темы Модель синхронного программирования Паттерны асинхронного программирования на платформе .Net Недостатки существующих моделей Библиотека PowerThreading Библиотека TPL C# 5.0: async и await
  13. 13. Асинхронная модельпрограммирования Два паттерна асинхронного программирования  Classical Async Pattern  Event-Based Async Pattern
  14. 14. 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);
  15. 15. Classical Async Pattern Метод BeginXXX инициирует асинхронную операцию Метод BeginXXX возвращает IAsyncResult, который является маркером асинхронной операции AsyncCallback представляет собой функцию, которая будет вызвана при завершении операции State представляет собой любой пользовательский объект
  16. 16. Classical Async Pattern Все by-value и by-ref параметры синхронного метода становятся by-value параметрами BeginXXX метода Тип возвращаемого значения синхронного метода совпадает с типом возвращаемого значения EndXXX метода ref и out параметры синхронного метода добавляются в EndXXX метод Исключения, генерируемые синхронным методом, генерируются методом EndXXX
  17. 17. Примеры 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);
  18. 18. Асинхронное выполнениеsd ClassicAsync Form Client HttpWebRequest IO completion webRequest1.BeginGetResponse() webRequest2.BeginGetResponse() webRequest2.ProcessResponse() ProcessResponse() UpdateUIElements() webRequest1.ProcessResponse() ProcessResponse() UpdateUIElements()
  19. 19. Classical Async Pattern В обработчике UI события:  Проинициализировать дополнительные поля (теперь без них не обойтись)  Сделать неактивной кнопку  В цикле начать все операции асинхронно (вызывать метод BeginGetResponse)
  20. 20. Classical Async Pattern В обработчике завершения асинхронной операции:  Получить WebResponse (вызывать метод EndGetReponse)  Обработать ответ, не забывая, что обработка происходит не в потоке UI  Понять, что все асинхронные операции завершены (??) (посчитать количество завершенных операций)  Сделать активной кнопку, если все операции таки завершены  Если перед началом выполнения асинхронных операций были выделены ресурсы, то освободить их вручную
  21. 21. Разница синхронного иасинхронного вариантов длятрех веб-узлов Среднее выполнение синхронного варианта: 520мс Среднее выполнение асинхронного варианта: 280 мс Количество строк кода в синхронном решении: 20 Количество строк кода в асинхронном решении: 78
  22. 22. Event-Based Async Pattern
  23. 23. 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
  24. 24. Event-Based Async Pattern MethodAsync инициирует асинхронную операцию Есть только одно событие MethodCompleted, EventArgs которого содержат Result, Error, IsCanceled Возможна, но не обязательна, поддержка отмены и прогресса выполнения
  25. 25. Event-Based Async Pattern By-value и ref параметры синхронного метода являются входными параметрами метода MethodAsync Ref и out-параметры становятся readonly полями MethodCompletedEventArgs Событие MethodCompleted вызывается в «правильном» потоке (в потоке UI для WinForms или WPF)
  26. 26. 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. // Обработка результатов }; }}
  27. 27. Да как же между нимивыбрать? Классический паттерн более низкоуровневый и более гибкий Event-Based паттерн более простой в применении, в частности с UI дизайнерами Классический паттерн – для кода общего назначения, Event-Based – для компонентов Не применяйте оба паттерна для одного класса
  28. 28. Рассматриваемые темы Модель синхронного программирования Паттерны асинхронного программирования на платформе .Net Недостатки существующих моделей Библиотека PowerThreading Библиотека TPL C# 5.0: async и await
  29. 29. Недостатки моделейасинхронного программирования Непонятный поток исполнения (Control Flow) Сложность чтения кода и его сопровождения Сложность обработки ошибок Невозможность использования привычных языковых конструкций (using, try/finally etc)
  30. 30. Рассматриваемые темы Модель синхронного программирования Паттерны асинхронного программирования на платформе .Net Недостатки существующих моделей Библиотека PowerThreading Библиотека TPL C# 5.0: async и await
  31. 31. PowerThreading Library Разработана Джеффри Рихтер и компанией Wintellect Содержит класс AsyncEnumerator для упрощения работы с асинхронными операциями Содержит вспомогательные классы для работы с многопоточностью (ResourceLock, ReaderWriterGate etc) Другие вспомогательные классы (Disposer, Exception<T>, Singleton etc)
  32. 32. Класс AsyncEnumerator.Основные концепции Поддерживает гибкое управление асинхронными операциями (отмену, таймауты и т.п.) Использует преимущество блока итераторов (Iterator block) для упрощения потока исполнения Использует SynchronizationContext для маршалинга потока выполнения в поток UI
  33. 33. Класс 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);
  34. 34. Отступление от темы. Блокиитераторов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");}
  35. 35. Визуализация итераторов 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()
  36. 36. Визуализация итераторов 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
  37. 37. Визуализация итераторов 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
  38. 38. Визуализация итераторов 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)
  39. 39. AsyncEnumerator. Участники Класс AsyncEnumerator Рабочий метод, возвращающий IEnumerator<int> Кто-то, связывающий все это воедино
  40. 40. AsyncEnumeratorsd AsyncEnumerator AsyncEnumerator WorkerMethod WebRequest IO Completion calling to MoveNext() webRequest1.BeginGetResponse() Первый вызов MoveNext енумератора инициирует ас инхронные операции. webRequest2.BeginGetResponse() WebRequest2Complete() WebRequest1Complete() calling to MoveNext() Т еперь рабочий метод В качес тве метода обработки (WorkerMethod) может обработать завершения ас инхронной завершенные операции и начать операции передаетс я метод новые ас инхронные операции. клас с а AsyncEnumerator-а.
  41. 41. Пример использования класса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()); // Обрабатываем полученные результаты аналогичным образом}
  42. 42. Пример использования классаAsyncEnumeratorprivate void receiveDataButton_Click(object sender, EventArgs e){ asyncEnumerator = new AsyncEnumerator(); // AsyncEnumerator автоматически запоминает контекст синхронизации // Запускаем процесс получения данных асинхронно asyncEnumerator.BeginExecute(GetWebData(asyncEnumerator), asyncEnumerator.EndExecute);}
  43. 43. Рассматриваемые темы Модель синхронного программирования Паттерны асинхронного программирования на платформе .Net Недостатки существующих моделей Библиотека PowerThreading Библиотека TPL C# 5.0: async и await
  44. 44. Библиотека TPL Параллелизм задач Класс Parallel (Parallel.ForEach etc) Parallel LINQ (a.k.a. PLINQ)
  45. 45. Ключевые классы Task – инкапсулирует единицу работы Task<TResult> - единица работы с определенным типом результата TaskFactory, TaskFactory<TResult> – фабрики создания задач TaskScheduler – управляет «расписанием» запуска задач TaskCompletionSource – управляет «временем жизни» задачи
  46. 46. Класс Task<T> Представляет собой «незавершенную операцию» или «единицу работы» Это может быть операция ввода/вывода, операция в фоновом потоке, в выделенном потоке и т.д. Поддерживает «продолжения» с помощью ContinueWithvar task2 = task1.ContinueWith(t => … t.Result …); Содержит ряд вспомогательных методов: WhenAll, WhenAny etc
  47. 47. Примеры использования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);
  48. 48. Примеры использования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;
  49. 49. Продолжения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("Все задачи завершены");
  50. 50. Продолжения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("Все задачи завершены"); Вывод: (ничего не происходит, поскольку задача еще не запущена)
  51. 51. Продолжения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("Все задачи завершены"); Вывод: (все еще ничего не происходит, поскольку первая задача не запущена) (первой задаче установлена вторая задача в виде продолжения)
  52. 52. Продолжения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("Все задачи завершены"); Вывод: (все еще ничего не происходит, поскольку первая задача не запущена) (Связке первых двух задач установлена третья задача в виде продолжения)
  53. 53. Продолжения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("Все задачи завершены"); Вывод: Запускаем первую задачу
  54. 54. Продолжения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("Все задачи завершены"); Вывод: Запускаем первую задачу Ожидаем завершения цепочки задач
  55. 55. Продолжения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...
  56. 56. Продолжения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...
  57. 57. Продолжения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...
  58. 58. Продолжения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... Все задачи завершены
  59. 59. Рассматриваемые темы Модель синхронного программирования Паттерны асинхронного программирования на платформе .Net Недостатки существующих моделей Библиотека PowerThreading Библиотека TPL C# 5.0: async и await
  60. 60. Модель асинхронногопрограммирования в C# 5 Аналогична синхронной модели Построена по тому же принципу, что и класс AsyncEnumerator Использует Task<T> для асинхронных операций Никто не знает, когда выйдет C# 5 Сейчас доступно Async CTP (Community Technology Preview)
  61. 61. Модель асинхронногопрограммирования в C# 5 Два новых ключевых слова:  async – указывает, что метод или лямбда- выражение является асинхронным  await – является аналогом yield return и возвращает сразу же управление вызывающему коду до тех пор, пока асинхронная операция не будет завершена
  62. 62. Модель асинхронногопрограммирования в 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;}
  63. 63. Работа простого метода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 (Асинхронная операция запущена)
  64. 64. Работа простого метода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 (Эта строка выполнится только после завершения операции)
  65. 65. Работа простого метода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
  66. 66. Сравнение с AsyncEnumerator IEnumerator<T> -> async Task<T> yield return n -> await task
  67. 67. Модель асинхронногопрограммирования в C# 5 Асинхронный метод может возвращать  void – для асинхронных операций типа “fire and forget”  Task – вызывающий код может дождаться завершения асинхронной операции, которая не возвращает значения  Task<T> - для асинхронной операции, возвращающей T (string для Task<string> etc)
  68. 68. Модель асинхронногопрограммирования 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();}
  69. 69. Преимущества новойасинхронной модели Простота использования Привычный поток исполнения Простота обработки ошибок и возможность использования конструкций using, try/finally etctry { WebResponse[] data = await TaskEx.WhenAll(tasks); // Обработка данных}catch (WebException we) { //Обработка ошибки получения данных}
  70. 70. Преимущества новойасинхронной модели Построена на основе проверенных идиом (Iterator blocks, AsyncEnumerator, Reactive Extensions) Построена на основе TPL (преимущества от ее использования можно закладывать уже сейчас)
  71. 71. Что мы изучили? Модель синхронного программирования Паттерны асинхронного программирования на платформе .Net Недостатки существующих моделей Библиотека PowerThreading Библиотека TPL C# 5.0: async и await
  72. 72. Дополнительные ссылки 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)
  73. 73. Дополнительные ссылки Итераторы в языке программирования 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)
  74. 74. Вопросы?

×