Тонкости асинхронного программирования

40
1 Luxoft Training 2013 06 июля 2013 Тонкости асинхронного программирования Dev Labs

Upload: sergey-teplyakov

Post on 10-May-2015

927 views

Category:

Documents


2 download

TRANSCRIPT

Page 1: Тонкости асинхронного программирования

1 © L

uxof

t Tra

inin

g 20

13

06 июля 2013

Тонкости асинхронного программирования

Dev Labs

Page 2: Тонкости асинхронного программирования

2 © L

uxof

t Tra

inin

g 20

13

Эволюция языка C#

Page 3: Тонкости асинхронного программирования

3 © L

uxof

t Tra

inin

g 20

13

Sync vs Async

SynchronousPerform something here and now.I’ll regain control to execute something else when it’s done.

Page 4: Тонкости асинхронного программирования

4 © L

uxof

t Tra

inin

g 20

13

Sync vs Async

Caller Srv

Foo

42

Caller Srv

Operation finishedResult: 42

FooAsync

Operation Started

Bckgn

Starting background operation

Notifies

t

t

t0

Sync vs Async

Page 5: Тонкости асинхронного программирования

5 © L

uxof

t Tra

inin

g 20

13

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();     } }

Page 6: Тонкости асинхронного программирования

6 © L

uxof

t Tra

inin

g 20

13

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();     } }

Page 7: Тонкости асинхронного программирования

7 © L

uxof

t Tra

inin

g 20

13

Demo – sync 2 async

Page 8: Тонкости асинхронного программирования

8 © L

uxof

t Tra

inin

g 20

13

Если вы думаете, что асинхронное программирование

стало проще!

Page 9: Тонкости асинхронного программирования

9 © L

uxof

t Tra

inin

g 20

13

Копайте в глубь!

Все нетривиальные абстракции дырявыДжоэл Спольски «Закон дырявых абстракций»

Вы должны понимать как минимум на один уровень абстракции ниже того уровня, на котором вы кодируетеЛи Кэмпбел (Lee Campbell)

Page 10: Тонкости асинхронного программирования

10 © L

uxof

t Tra

inin

g 20

13

Async/await – лишь вершина

Page 11: Тонкости асинхронного программирования

11 © L

uxof

t Tra

inin

g 20

13

Synchronization Context

Некоторые типы приложений налагают ограничения на «потоковую» модель

Control.Invoke/BeginInvoke

Dispatcher.Invoke/BeginInvoke

Контекст синхронизации «прячет» эти детали за абстрактным интерфейсом

Контекст нужен для «маршалинга» управления из одного потока в другой (*)

Контексты повсюду!

Page 12: Тонкости асинхронного программирования

12 © L

uxof

t Tra

inin

g 20

13

Зачем мне это?Я же программирую на C#

5.0!

Page 13: Тонкости асинхронного программирования

13 © L

uxof

t Tra

inin

g 20

13

private int GetAnswerToUltimateQuestion(){    buttonRun.Enabled = false;    Thread.Sleep(1000);    buttonRun.Enabled = true;    return 42;}

private void buttonRun_Click_Sync(object sender, EventArgs e){    int result = GetAnswerToUltimateQuestion();    textBoxStatus.Text = result.ToString();}

Sync Version

Некоторая длительная операция!!

Page 14: Тонкости асинхронного программирования

14 © L

uxof

t Tra

inin

g 20

13

private async Task<int> GetAnswerToUltimateQuestionAsync(){    buttonRun.Enabled = false;    await Task.Delay(1000); buttonRun.Enabled = true;

    return 42;

}

private void buttonRun_Click_Async(object sender, EventArgs e){    Task<int> result = GetAnswerToUltimateQuestionAsync();    textBoxStatus.Text = result.Result.ToString();}

Наивный подход…

[Thread 2] Ожидает

освобождения UI потока

[Thread 1] Ожидает

завершения асинхронной

операции

Захватываем Sync Context

Возвращает управление!

Page 15: Тонкости асинхронного программирования

15 © L

uxof

t Tra

inin

g 20

13

Другими словами…private Task<int> GetAnswerToUltimateQuestionAsyncImpl(){    buttonRun.Enabled = false;    var task = LongRunningTask();    return task.ContinueWith(t =>        {            buttonRun.Enabled = true;            return 42;        }, TaskScheduler.FromCurrentSynchronizationContext());}

private void buttonRun_Click_Async_Impl(object sender, EventArgs e){    Task<int> result = GetAnswerToUltimateQuestionAsyncImpl();    textBoxStatus.Text = result.Result.ToString();}

Page 16: Тонкости асинхронного программирования

17 © L

uxof

t Tra

inin

g 20

13

“Sync over Async” + UI == Deadlock!

Page 17: Тонкости асинхронного программирования

18 © L

uxof

t Tra

inin

g 20

13

Решение

private async Task<int> GetAnswerAsync() {     await Task.Delay(10);     return 42; }

private async void btn_Click(object sender,  EventArgs e) {     label.Text = (await GetAnswerAsync()).ToString(); }

Page 18: Тонкости асинхронного программирования

19 © L

uxof

t Tra

inin

g 20

13

Где вылетит ошибка?

var ms = new MemoryStream();           

// Здесь? Task<int> task = ms.ReadAsync(null, 0, 42);

// Или здесь? int result = task.Result;

• Является исключение «синхронным» или «асинхронным»?

Page 19: Тонкости асинхронного программирования

20 © L

uxof

t Tra

inin

g 20

13

«Наивная» реализация

public static async Task<int> ReadAsync(byte[] buffer) {     if (buffer == null)          throw new ArgumentNullException("buffer");

    // Реализация асинхронного чтения     return 42; }

Результирующая задача перейдет в Faulted

состояние!

• Синхронное исключение означает «баг» в вызывающем коде.

• «Поломанная» задача означает баг в реализации!

Зачем заморачиваться?

Page 20: Тонкости асинхронного программирования

21 © L

uxof

t Tra

inin

g 20

13

Корректная реализация

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; }

Синхронная проверка

«предусловий»

Блоки итераторов ведут себя

аналогично!

Page 21: Тонкости асинхронного программирования

22 © L

uxof

t Tra

inin

g 20

13

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!!!");     } }

Page 22: Тонкости асинхронного программирования

23 © L

uxof

t Tra

inin

g 20

13

Обработка нескольких исключений

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?UnobservedTaskExcep

tion!

Page 23: Тонкости асинхронного программирования

24 © L

uxof

t Tra

inin

g 20

13

Unobserved Exceptions

Событие TaskScheduler.UnobservedException

Генерируется финализатором

Не вызывается при обращении к

Result

Exception

Вызове Wait

Поведение зависит от версии .NET Framework

.NET 4.5 – «умалчивается» (*)

.NET 4.0 – «ломает» приложение

Page 24: Тонкости асинхронного программирования

25 © L

uxof

t Tra

inin

g 20

13

“Решение”

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!!!!

Page 25: Тонкости асинхронного программирования

26 © L

uxof

t Tra

inin

g 20

13

Структура исключений

AggregateException

task.ContinueWith()

AggregateException

Task.WhenAll(t1, t2);

First

Exception("E1")

t1: Task

Exception("E2")

t2: Task

First Second

await вытянет первое исключение и получит AggregateException!

Task.WhenAll(t1, t2);

await task.ContinueWith(_ => _.Result);

Page 26: Тонкости асинхронного программирования

27 © L

uxof

t Tra

inin

g 20

13

И как это дело ловить?

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) {}

Page 27: Тонкости асинхронного программирования

28 © L

uxof

t Tra

inin

g 20

13

Асинхронные методы

Типы возвращаемого значения асинхронного метода:

async void FooAasync() – Fire and Forget (*)

async Task FooAsync() – (void Foo())

async Task<T> FooAsync() – (T Foo())

Page 28: Тонкости асинхронного программирования

29 © L

uxof

t Tra

inin

g 20

13

Где вылетит ошибка?

private static async void FooAsync(){    throw new Exception("Ooops!");}

It Depends!

Page 29: Тонкости асинхронного программирования

30 © L

uxof

t Tra

inin

g 20

13

Demo – async void exceptions

Page 30: Тонкости асинхронного программирования

31 © L

uxof

t Tra

inin

g 20

13

Задача: убедиться, что метод генерирует исключение!

Page 31: Тонкости асинхронного программирования

32 © L

uxof

t Tra

inin

g 20

13

Naïve Approachpublic static async Task FooAsync(){    //throw new InvalidOperationException("Oops!");    await Task.Delay(42);}[Test]public async void Test_FooAsync_Throws_1(){    try    {        await FooAsync();        Assert.Fail("InvalidOperation was not thrown");    }    catch (InvalidOperationException)    { }}

[Test]public async void Test_FooAsync_Throws_2(){    Assert.Throws<InvalidOperationException>(async () => await FooAsync());}

Page 32: Тонкости асинхронного программирования

33 © L

uxof

t Tra

inin

g 20

13

Рабочий вариант![Test]public void Test_FooAsync_Throws(){    // Sync over Asyc? ;)    ThrowsAsync<InvalidOperationException>(FooAsync).Wait();}

public async static Task ThrowsAsync<T>(Func<Task> testCode)  where T : Exception{    try    {        await testCode();        // Если мы сюда попадем, то движок NUnit         // бросит нужный тип исключения        Assert.Throws<T>(() => { });    }    catch (T)    {}}

Page 33: Тонкости асинхронного программирования

34 © L

uxof

t Tra

inin

g 20

13

Async Guidelines

Async void – это операции вида “fire-and-forget”

Вызывающий код не может узнать о завершении асинхронного метода

Вызывающий код не может обработать исключения(вместо этого они попадут в цикл обработки UI сообщений или «уронят» приложение!)

Используйте async void только для обработчиков событий самого высокого уровня.

Используйте возвращаемые значения!

Осторожнее с асинхронными лямбда-выражениями!

Page 34: Тонкости асинхронного программирования

35 © L

uxof

t Tra

inin

g 20

13

Сколько же тут всего…

Влияние TAP на дизайн приложения!

Обработка исключений Unobserved exceptions

Bugs vs Task Faults

Гранулярность асинхронных операций

Testability

Work stealing

Page 35: Тонкости асинхронного программирования

36 © L

uxof

t Tra

inin

g 20

13

Вот этого не надо

- Как вы пишите софт?- Бац-бац и в продакшн (с).

- Как из синхронного приложения сделать асинхронное?- Async/await и готово!

Page 36: Тонкости асинхронного программирования

37 © L

uxof

t Tra

inin

g 20

13

Его высочество Async …

не так прост, как кажется;)

Page 37: Тонкости асинхронного программирования

38 © L

uxof

t Tra

inin

g 20

13

Что думает по этому поводу 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.

Page 38: Тонкости асинхронного программирования

39 © L

uxof

t Tra

inin

g 20

13

Дополнительные ссылки

pfxteam blog

C# Async Tips and Tricks Part 2: Async Void

MVP Summit presentation on async

Знакомство с асинхронными операциями в C# 5

Page 39: Тонкости асинхронного программирования

40 © L

uxof

t Tra

inin

g 20

13

Благодарю за внимание!

Вопросы?

Page 40: Тонкости асинхронного программирования

41 © L

uxof

t Tra

inin

g 20

13

IntHRLuxtown

Информация об учебном центреwww.luxoft-training.ru/about

Расписание www.luxoft-training.ru/timetable

Каталог курсов www.luxoft-training.ru/training/catalog_directions

Контакты www.luxoft-training.ru/contacts

www.facebook.com/TrainingCenterLuxoft

Расписание, курсы,

тренеры

Условия обучения, логистика,

контакты

Luxtown

Информационные ресурсы Luxoft Training