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. 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. 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. Копайте в глубь!
Все нетривиальные абстракции дырявы
Джоэл Спольски «Закон дырявых абстракций»
Вы должны понимать как минимум на один
уровень абстракции ниже того уровня, на
котором вы кодируете
Ли Кэмпбел (Lee Campbell)
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. 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. 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. Synchronization Context
• Некоторые типы приложений налагают ограничения на
«потоковую» модель
• Control.Invoke/BeginInvoke
• Dispatcher.Invoke/BeginInvoke
• Контекст синхронизации «прячет» эти детали за
абстрактным интерфейсом
• Контекст нужен для «маршалинга» управления из одного
потока в другой (*)
• Контексты повсюду!
20. Где вылетит ошибка?
var ms = new MemoryStream();
// Здесь?
Task<int> task = ms.ReadAsync(null, 0, 42);
// Или здесь?
int result = task.Result;
• Является исключение «синхронным» или «асинхронным»?
21. «Наивная» реализация
public static async Task<int> ReadAsync(byte[] buffer)
{
if (buffer == null)
throw new ArgumentNullException("buffer");
// Реализация асинхронного чтения
return 42;
}
Результирующая задача перейдет в
Faulted состояние!
• Синхронное исключение означает «баг» в
вызывающем коде.
• «Поломанная» задача означает баг в реализации!
Зачем заморачиваться?
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. Unobserved Exceptions
• Событие TaskScheduler.UnobservedException
• Генерируется финализатором
• Не вызывается при обращении к
• Result
• Exception
• Вызове Wait
• Поведение зависит от версии .NET Framework
• .NET 4.5 – «умалчивается» (*)
• .NET 4.0 – «ломает» приложение
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. “Решение”
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. И как это дело ловить?
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. Асинхронные методы
• Типы возвращаемого значения асинхронного метода:
• async void FooAasync() – Fire and Forget (*)
• async Task FooAsync() – (void Foo())
• async Task<T> FooAsync() – (T Foo())
31. Async Guidelines
• Async void – это операции вида “fire-and-forget”
• Вызывающий код не может узнать о завершении
асинхронного метода
• Вызывающий код не может обработать исключения
(вместо этого они попадут в цикл обработки UI
сообщений)
• Используйте async void только для обработчиков событий
самого высокого уровня.
• Используйте возвращаемые значения!
• Осторожнее с асинхронными лямбда-выражениями!
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. Вот этого не надо
- Как вы пишите софт?
- Бац-бац и в продакшн (с).
- Как из синхронного приложения сделать
асинхронное?
- Async/await и готово!
35. Спасибо за внимание
• Сергей Тепляков, Visual C# MVP
• .NET Architect at Luxoft
• Sergey.Teplyakov@gmail.com
• http://sergeyteplyakov.blogspot.com/
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.
Hinweis der Redaktion
Просто для того, чтобы с чего-нибудь начать разговор и подойти к сегодняшней теме я бы хотел немного напомнить историю развития платформы .NET и языка C#. Это даст нам понять то, как же мы докатились до такой жизни.В целом, нас интересует два последних релиза языка C#, которые и дали основной толчок асинхронному программированию на платформе .NET.И да, вам не послышалось и я не оговорился, именно два последних релиза сделали асинхронное программирование таким, каким мы его знаем сегодня, а не один последний релиз.ВC# 4.0 (а точнее в .NET Framework 4.0) появилась TPL, а в C# 5.0 (и .NET 4.5) эта модель асинхронного программирования стала повсеместной; и не последнюю роль в этом вопросе сыграли новые возможности языков C# и VB.NET.Давайте посмотрим, к чему же мы стремились.
С одной стороны, асинхронное программирование на платформе .NET существовало с самой первой ее версии (в виде так называемой Asynchronous Programming Model), а с другой стороны асинхронное программирование всегда было на порядок сложнее с точки зрения реализации, отладки и сопровождения, по сравнению с синхронной версией.Основная же цель новых «асинхронных» возможностей языка C# была направлена на «унификацию» синхронного и асинхронного программирования. Решить проблему асинхронности путем добавления пары ключевых слов в тело и заголовок методов, чтобы сделать их асинхронными.И это просто прекрасно, но сразу же после выхода этого дела в свет стало ясно, что у этой простоты есть своя цена! На самом деле, я могу
TODO: выделить ключевые слова и Async, чтобы изменения были более видны
Абстракции текут и чтобы понимать что-то нам нужно разбираться на один уровень абстракции ниже!
Обязательно сказать и продумать про разные типы приложений!!
Когда речь заходит за UI потоки, то любой join асинхронной операции может привести к deadlock-у. В этом случае работает следующее правило: если вы начали использовать асинхронные операции, то их следует продолжать использование, а не ожидать их завершения (явно или неявно).
TODO:Рисунок
Что будет в этом случае? Будет ли проброшено исключение первой задачи, второй задачи, обеих задач в виде AggregateException?
Очень печально, но в данном случае наружу вылетит лишь первое из возникших исключений!Я так и не смог добиться получения наружу обоих исключений!!
PrinciplesAsync void is a “fire-and-forget” mechanism...The caller is unable to know when an async void has finishedThe caller is unable to catch exceptions thrown from an async void(instead they get posted to the UI message-loop)GuidanceUse async void methods only for top-level event handlers (and their like)Use async Task-returning methods everywhere elseWhen you see an async lambda, verify it
Сказать о том, что IO Bound операции вообще не требуют физических потоков ОС. Мы не просто усыпим поток ОС, мы вообще не будем их использовать, поскольку все, что нам потребуется, это создать небольшой объект (представляющий собой callback от ОС) и передать его соответствующей функции. Когда операция будет завершена (без участия ЦПУ), то этот callback будет вызван.
Речь идет об открытых методах! Прикладной разработчик и сам сможет запустить ее асинхронно (чтобы не блокировать UI), исходя из своих собственных требований.Когда же мы говорим о публичном интерфейсе сборки или о библиотеке, то мы должны понимать, что любая асинхронная версия подразумевает возможность ее многократного вызова без негативного влияния на производительность системы.