Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Async clinic by by Sergey Teplyakov

4,285 views

Published on

  • Be the first to comment

Async clinic by by Sergey Teplyakov

  1. 1. Асинхронное программирование Сергей Тепляков, Visual C# MVP .NET Architect at Luxoft SergeyTeplyakov.blogspot.com
  2. 2. Sync vs Async Synchronous Perform something here and now. I’ll regain control to execute something else when it’s done. Asynchronous
  3. 3. Эволюция языка C#
  4. 4. Sync vs Async Caller Srv Foo 42 Caller Srv Operation finished Result: 42 FooAsync Operation Started Bckgn Starting background operation Notifies t t t0 Sync vs Async
  5. 5. Sync vs Async var webRequest = WebRequest.Create(Url); using (var response = webRequest.GetResponse()) { using (var file = new FileStream(FileName, FileMode.OpenOrCreate)) { var length = response.ContentLength; var textWriter = new StreamWriter(file); textWriter.Write(length.ToString()); textWriter.Close(); } }
  6. 6. Sync vs Async (2) var webRequest = WebRequest.Create(Url); using (var response = await webRequest.GetResponseAsync()) { using (var file = new FileStream(FileName, FileMode.OpenOrCreate)) { var length = response.ContentLength; var textWriter = new StreamWriter(file); await textWriter.WriteAsync(length.ToString()); textWriter.Close(); } }
  7. 7. Копайте в глубь! Все нетривиальные абстракции дырявы Джоэл Спольски «Закон дырявых абстракций» Вы должны понимать как минимум на один уровень абстракции ниже того уровня, на котором вы кодируете Ли Кэмпбел (Lee Campbell)
  8. 8. Async/await – лишь вершина
  9. 9. Async/Await Sample public async Task AsyncMethod(string url, string fileName) { var webRequest = WebRequest.Create(url); Task<WebResponse> tResp = webRequest.GetResponseAsync(); WebResponse response = await tResp; var responseStream = response.GetResponseStream(); var tr = new StreamReader(responseStream); Task<string> tContent = tr.ReadToEndAsync(); string content = await tContent; var file = File.OpenWrite(fileName); var streamWriter = new StreamWriter(file); Task writeResult = streamWriter.WriteAsync(content); await writeResult; Console.WriteLine("AsyncMethod finished"); }
  10. 10. Async/Await Sample Код Поток var webRequest = WebRequest.Create(url); Task<WebResponse> tResp = webRequest.GetResponseAsync(); Вызывающий поток Await возвращает управление вызывающему коду WebResponse response = await tResp; var responseStream = response.GetResponseStream(); var tr = new StreamReader(responseStream); Task<string> tContent = tr.ReadToEndAsync(); Поток 2 Await возвращает управление вызывающему коду string content = await tContent; var file = File.OpenWrite(fileName); var streamWriter = new StreamWriter(file); Task writeResult = streamWriter.WriteAsync(content); Поток 3 Await возвращает управление вызывающему коду await writeResult; Console.WriteLine("AsyncMethod finished"); Поток 4 Метод завершен успешно
  11. 11. Async vs. Tasks public async Task SimpleAsync(string url) { var webRequest = WebRequest.Create(url); Task<WebResponse> tResp = webRequest.GetResponseAsync(); WebResponse response = await tResp; Console.WriteLine("Got the response"); } public Task AsyncMethodImpl(string url) { var webRequest = WebRequest.Create(url); Task<WebResponse> tResp = webRequest.GetResponseAsync(); return tResp.ContinueWith(t => { Console.WriteLine("Got the response"); }, TaskScheduler.FromCurrentSynchronizationContext()); }
  12. 12. Synchronization Context • Некоторые типы приложений налагают ограничения на «потоковую» модель • Control.Invoke/BeginInvoke • Dispatcher.Invoke/BeginInvoke • Контекст синхронизации «прячет» эти детали за абстрактным интерфейсом • Контекст нужен для «маршалинга» управления из одного потока в другой (*) • Контексты повсюду!
  13. 13. Зачем мне это? Я же программирую на C# 5.0!
  14. 14. Что будет в этом случае? private async Task<int> Foo() { buttonRun.Enabled = false; await LongRunningTask(); buttonRun.Enabled = true; return 42; } private void btn_Click(object sender, EventArgs e) { label.Text = FooAsync().Result.ToString(); } «Продолжение» должно вызываться в UI потоке Ожидает завершения асинхронной операции Захватываем Sync Context Возвращает управление!
  15. 15. “Sync over Async” + UI == Deadlock!
  16. 16. Решение private async Task<int> Foo() { buttonRun.Enabled = false; await LongRunningTask(); buttonRun.Enabled = true; return 42; } private async void buttonRun_Click(object sender, EventArgs e) { textBoxStatus.Text = ""; textBoxStatus.Text = (await Foo()).ToString(); }
  17. 17. Паттерны асинхронного программирования • Asynchronous Programming Model (APM) • BeginInvoke/EndInvoke • Event-Based Async Pattern (EAP) • BackgroundWorker: RunWorkerAsync, RunWorkerCompleted • Bicycle –Async Pattern (BAP) • Кастомные решения • Task-Based Async Pattern (TAP) • Task<int> ReadAsync
  18. 18. Exception Handling Task.Result Task.Wait await task; FirstException AggregateExceptionfails fails
  19. 19. Demo. Exception Handling
  20. 20. Где вылетит ошибка? var ms = new MemoryStream(); // Здесь? Task<int> task = ms.ReadAsync(null, 0, 42); // Или здесь? int result = task.Result; • Является исключение «синхронным» или «асинхронным»?
  21. 21. «Наивная» реализация public static async Task<int> ReadAsync(byte[] buffer) { if (buffer == null) throw new ArgumentNullException("buffer"); // Реализация асинхронного чтения return 42; } Результирующая задача перейдет в Faulted состояние! • Синхронное исключение означает «баг» в вызывающем коде. • «Поломанная» задача означает баг в реализации! Зачем заморачиваться?
  22. 22. Корректная реализация public static Task<int> ReadAsync(byte[] buffer) { if (buffer == null) throw new ArgumentNullException("buffer"); return ReadAsyncImpl(buffer); } private static async Task<int> ReadAsyncImpl(byte[] buffer) { // Реализация асинхронногочтения return 42; } Синхронная проверка «предусловий»
  23. 23. Demo. Exception Handling
  24. 24. Обработка нескольких исключений public static async Task FooAsync() { // t1 "падает" Task<int> t1 = Task<int>.Factory.StartNew(() => { throw new Exception("E1"); }); // t2 тоже "падает" Task<int> t2 = Task<int>.Factory.StartNew(() => { throw new Exception("E2"); }); int r1 = await t1; int r2 = await t2; } Получим “E1”? Получим “E2”? Получим AggregateException? UnobservedTaskException!
  25. 25. Unobserved Exceptions • Событие TaskScheduler.UnobservedException • Генерируется финализатором • Не вызывается при обращении к • Result • Exception • Вызове Wait • Поведение зависит от версии .NET Framework • .NET 4.5 – «умалчивается» (*) • .NET 4.0 – «ломает» приложение
  26. 26. Await исключений public static async Task<int> SimpleAsync() { throw new CustomException(); } public static async void ConsumeSimpleAsync() { var task = SimpleAsync(); try { // "Разыменовывание" задачи приводит к // "разворачиванию" первого исключения! int result = await task; } catch (CustomException) { Console.WriteLine("Yahoo!!!"); } }
  27. 27. “Решение” public static async Task FooAsync() { // t1 "падает" Task<int> t1 = Task<int>.Factory.StartNew(() => { throw new Exception("E1"); }); // t2 тоже "падает" Task<int> t2 = Task<int>.Factory.StartNew(() => { throw new Exception("E2"); }); // "Наблюдаем" оба исключения var task = Task.WhenAll(t1, t2); await task.ContinueWith(_ => _.Result); int r1 = t1.Result; int r2 = t2.Result; } «Объединяем» обе задачи await task; пробросил бы лишь первое исключение!Явно генерируем AggregateException!!!!
  28. 28. И как это дело ловить? var task = Modified.FooAsync(); try { await task; } // Для случая более одного исключения catch (AggregateException e) { // "Выпрямляем" все исключения int count = e.Flatten().InnerExceptions.Count; Console.WriteLine( "Demo2.Modified.FooAsync failed with {0} exceptions", count); } // Для более простых случаев catch (CustomException e) { } catch (Exception e) {}
  29. 29. Асинхронные методы • Типы возвращаемого значения асинхронного метода: • async void FooAasync() – Fire and Forget (*) • async Task FooAsync() – (void Foo()) • async Task<T> FooAsync() – (T Foo())
  30. 30. Demo. Async Void
  31. 31. Async Guidelines • Async void – это операции вида “fire-and-forget” • Вызывающий код не может узнать о завершении асинхронного метода • Вызывающий код не может обработать исключения (вместо этого они попадут в цикл обработки UI сообщений) • Используйте async void только для обработчиков событий самого высокого уровня. • Используйте возвращаемые значения! • Осторожнее с асинхронными лямбда-выражениями!
  32. 32. Сколько же тут всего… • Влияние TAP на дизайн приложения! • Обработка исключений • Unobserved exceptions • Bugs vs Task Faults • Гранулярность асинхронных операций • Testability • Work stealing • Async Programming Guidelines • Avoid “async void” • Avoid async lambdas • Consider performance impact • Avoid “Sync over Async” • Avoid “Async over Sync” • Consider using ConfigureAwait
  33. 33. Вот этого не надо - Как вы пишите софт? - Бац-бац и в продакшн (с). - Как из синхронного приложения сделать асинхронное? - Async/await и готово!
  34. 34. Вопросы?
  35. 35. Спасибо за внимание • Сергей Тепляков, Visual C# MVP • .NET Architect at Luxoft • Sergey.Teplyakov@gmail.com • http://sergeyteplyakov.blogspot.com/
  36. 36. Что думает по этому поводу Eric Lippert? Q: C# 5.0 has new feature called async/await. Why should developers be excited about it? A: People like me, are excited about this feature because its a cooperative multitasking with coroutines implementing using continuation passing style.

×