Asynchronous programming in .NET (UA)

Post on 18-Jul-2015

901 views 1 download

Transcript of Asynchronous programming in .NET (UA)

Асинхронне програмування в .NET

Олександр Павлишак

травень 2012

Для кого

Початківці

Ідея асинхронності

Базові техніки

Досвідчені

Поточний стан справ

Нові бібліотеки та підходи

Структуризація, порівняння

Outside .NET

Нові ідеї, розширення світогляду

План

Асинхронність intro

Asynchronous Programming Model (APM)

Tasks (Task Parallel Library)

Reactive Extensions (Rx)

C# 5.0 async

Асинхронність

– Ініціювати виконання операції

– Не чекати поки операція закінчиться,

– а одразу повернути виконання

– Продовжувати займатись іншими справами

– Могти скасувати операцію

– Отримати сповіщення про результат операції (успішний/неуспішний)(callback)

== відсутність блокування

На клієнті

UI потік не можна блокувати

Типові асинхронні операції:

– Запити до серверів (веб, БД, sockets) – IO bound

– Читання/запис на диск – IO bound

– Довготривалі обчислення – CPU bound

Основна незручність:

– результат асинхронної операції повинен бути оброблений в UI потоці

Основна мета – responsiveness

Бажане функціонування UI потоку

– Багато дрібних подій, виконання кожної з яких швидко завершується

– Події – message pump events + async operations callbacks

– Новим подіям не приходиться довго чекати на початок виконання

Hardware працює асинхронно

CPU не блокується на операціях з пам’яттю, диском, мережевим адаптером

Всі IO пристрої володіють затримкою (latency)

Непередбачуваною

Підтримка OS: Completion ports

Недолік –програмна модель досить складна

O RLY?

IO-bound задачі можуть виконуватись фоново (на апаратному рівні)

Як наслідок:

Не потрібні лишні потоки програми з логікою обробки результатів IO

Цим може займатись UI потік,

якщо ця обробка швидка.

Потрібна зручна абстракціяCompletion ports занадто низькорівневі

Плюс є ще CPU-bound задачі

На сервері

Задача – обробляти багато клієнтських запитів одночасно

(concurrency)

Модель IIS + ASP.NET: 1 request – 1 thread1000 запитів – 1000 потоків

1000 запитів – 30 потоків + 970 запитів в черзі

Оптимальна кількість потоків = кількості CPU

Неефективне використання ресурсів:Більшість часу потоки чекають на IO

А коли прокидаються, конкурують за CPU

Основна мета – масштабованість (scalability)

Responsiveness and scalabilityдві мети асинхронного програмування

Callbacksзасіб реалізації, який володіє проблеми :(

Потрібне розширення абстракції callback’a,а також кращі абстракції замість callback’ів

Простий випадок: виконуємо одну операцію асинхронно(лише happy path)

Demo:

UI (load/save values)APM (Asynchronous Programming Model)

Альтернативи: окремий потік + UI через dispatcher

ASP.NET (call web service)APM

приклади тут

stream.BeginRead(buffer, 0, buffer.Length, asyncResult =>{

context.Post(_ =>{

try{

stream.EndRead(asyncResult);stream.Dispose();Input = Encoding.ASCII.GetString(buffer);

}catch{

Input = "Error!!!";}finally{

DisplayLoadCompletedNotification();}

}, null);}, null);

Can throw

Execute in UI thread

Does not block

Does not block here

Captured in closure

“Awesomeness” of callbacks

Все ускладнюється

Обробка помилок

Cancellation

Запуск багатьох операцій послідовно

Запуск декількох операцій паралельно+ після закінчення сповістити користувача

Координація операційпісля закінчення двох викликів запустити третій

...));}}}));

Wait, this is not LISP!

APM

Запуск: fileStream.BeginRead(…)

Callback: BeginRead(…, asyncResult => { … }, …)Викликає EndRead()

SynchronizationContext – щоб повернутись в UI потік

Результат: виклик EndRead()

Exceptions: виклик EndRead()try…catch навколо EndRead() всередині callback

Де:Thread from IO thread pool

Явне використання SynchronizationContext.Post()

Composition: :(

Futures and Promises

Розділення ініціювання операції від отримання результату

Func<>, Action<> – навпаки

Реалізація – Task<T>Обох понять, future and promise

Обох видів, IO-bound and CPU-bound

Block on Task.Result

Continuation with Task.ContinueWith()

Task.Result повідомляє про Exception

RestoreInput().ContinueWith(restoreInputTask =>

{try{

Input = restoreInputTask.Result;}catch{

Input = "Error!!!";}finally{

DisplayLoadCompletedNotification();}

}, TaskScheduler.FromCurrentSynchronizationContext());

Returns Task<string> Called when task completes

Does not block Rethrows

Where to execute continuation

Already started

In UI thread

Task returned by RestoreInput()

We can schedule several continuations

Task<string> RestoreInput(){

return Task.Factory.StartNew(() =>{

return File.ReadAllText("savedInput.txt");});

}

Returns started task

Runs in thread poolby default

Exception will be re-thrown in Task.Result

Can specify Scheduler

Запуск Task’ів

TaskFactory.FromAsync()Адаптація APM-моделі до Task-моделі

TaskFactory.StartNew(() => { return …; })Адаптація будь-якої моделі до Task

Вказується TaskScheduler

Вказується CancellationToken

Demo

Save/load values with TPL

Tasks

Запуск: TaskFactory.FromAsync()TaskFactory.StartNew()

Callback: Task.ContinueWith()Викликає Task.Result

Результат: Task.Result in continuationExceptions: виклик Task.Result

try…catch навколо Task.Result всередині callbackTask.Exception, Task.IsFaulted, Task.Status

Де:При запуску вказується TaskSchedulerВ ContinueWith() вказується TaskScheduler

Composition: ContinueWith(), WhenAll/Any()

Events

Корисна абстракція, не лише для UIFirst class (F#), Delegates (C#), GOF-style Observers

Single events: button click, request received

Event streams: mouse moves,key presses, stream of tweets

Події часто асинхронніПроблема – погано компонуються

Unless first class

Уявіть композицію

Швидкий пошук, фільтр

Уявіть композицію

По суті – серія подій OnTextChanged

Рядок повинен обрізатись – String.Trim()Tabs spaces; multiple spaces single space

Рядки із спец. символами повинні виключатись

Запуск пошуку – коли користувач перестане друкувати – throttling

Пошук лише значень, які відрізняються(послідовних) – distinct

Відображення результату останнього пошуку а не того, який прийшов найпізніше

Запис в історію пошуку

IFs, IFs, IFs

Timer

Shared mutable var

More mutable state

Multiple subscribers

Reactive Extensions

Серія OnTextChanged = stream of TextBox.Texts

Звучить як... IEnumerable<string>?Pull-based: T MoveNext(void)

IObservable<string>!Push-based: void OnNext(T)

Functional Programming! Monads! Composition! Pure functions! Say No to Mutable State! Support cancer research!

Lambda

Observer ≈≈ Iterator

interface IEnumerator<out T>{

T Current { get; }

bool MoveNext(void);}

interface IObserver<in T>

{

void OnNext(T);

void OnError(Exception);

void OnCompleted();

}

interface X<out T>{

T|Exception X(void)

bool X(void);}

interface X<in T>

{

void X(T);

void X(Exception);

void X(bool);

}

Observer ≈≈ Iterator

interface IObservable<out T>

{

IDisposable Subscribe(Observer<T>);

}

interface IEnumerable<out T>

{

IEnumerator<T> GetEnumerator(void);

}

textChanges.Select(s => s.Trim()).Where(s => s != "").Subscribe(onNext:

s => Console.WriteLine(s));

IObservable<string>IObservable<string>

Runs on each received string

Can also pass onError, onCompleted

Returns IDisposable for un-subscription

Reactive Extensions (Rx)

A library for composing asynchronous and event-based programs using observable

sequences and LINQ-style query operators.

Rx = Observables + LINQ + Schedulers

Official site

Demo

Single event – Save/Load settings

Event stream – TextBox.Text changes

Rx

Запуск: багато шляхів, включаючи:Observable.Return(), Observable.FromAsyncPattern()

Callback: IObserver.OnNext()IObservable.Subscribe(item => { … })

Результат: IObserver.OnNext()

Exceptions:IObserver.OnError(Exception)

Catch(), Finally(), OnErrorResumeNext() combinators

Де:ObserveOn(IScheduler)

Composition: широкий набір комбінаторів, LINQ

Rx – функціональний шлях вирішення проблем з асинхронністю

та concurrency

Як щодо імперативного стилю?

Monads! Composition! Purity!

Імперативний стиль

Наскільки sync код відрізняється від async?

Суттєво відрізняється!

хіба що ви програмували у функціональному стилі з самого початку

Як конвертувати sync код в async?

Перетворювати в continuations – CPS

Як бути з while/for/foreach?

try…catch? finally? using() {…}?

hint hint :)

Після перетворення

Перетворений код буде

– Рекурсивний (tail recursion, anyone?)

– Реалізовувати машину станів (goto is back!)

Хороша новина: така трансформація є механічна

Compiler can do it automagicallyforeach

yield return

Pattern-based

foreach over non-IEnumerable

Імперативний sync код

try{

DisplayLoadingInProgressNotification();Input = RestoreInput();

}catch{

Input = "Error!!!";}finally{

DisplayLoadCompletedNotification();}

async

try{

DisplayLoadingInProgressNotification();Input = await RestoreInput();

}catch{

Input = "Error!!!";}finally{

DisplayLoadCompletedNotification();}

Ще приклад: цикл

try{

DisplayLoadingInProgressNotification();foreach (var textBox in InputTextBoxes){

try{

textBox.Text = RestoreInput(GetFileName(textBox));

}catch{ textBox.Text = "Error!!!"; }

}}finally{ DisplayLoadCompletedNotification(); }

async з циклом

try{

DisplayLoadingInProgressNotification();foreach (var textBox in InputTextBoxes){

try{

textBox.Text = await RestoreInput(GetFileName(textBox));

}catch{ textBox.Text = "Error!!!"; }

}}finally{ DisplayLoadCompletedNotification(); }

await non-thread-pool Task

async Task<string> RestoreInput(){

using (var reader = File.OpenText("savedInput.txt")){

var result = await reader.ReadToEndAsync();Debug.WriteLine("Input restored from file.");return result;

}}

await thread-pool Tasks

if (File.Exists(fileName))

return await Task.Run(() =>

{

return File.ReadAllText(fileName);

});

else

return String.Empty;

Demo

async save/load value

async save/load multiple values in sequence

async save/load multiple values in parallel

async, await

Запуск: call async APIReturning Task, WinRT or any awaitable

Callback: код, який слідує після awaitабо Task.ContinueWith()

Результат: await

Exceptions: await

Де: в поточному SynchronizationContextМожна також заборонити context capturing;

Composition: імперативний кодТакож Tasks

APM, Tasks, Rx, asyncAPM

- Callback model

- Композиція складна

Tasks+/- Callbacks

+ Краща композиція, обробка помилок, cancellation

Rx++ Композиція, обробка помилок та cancellationу функціональному стилі

- Learning curve

async+/- Композиція, обробка помилок в імперативному стилі

+ Конвертується в Tasks

Дякую за увагу!

TPL Home Team Blog Free book Other book

Reactive Extensions Home C9-Videos Intro

async Home Spec Jon Skeet InfoQ WinRT Deep

Презентація slideshare.net/opavlyshak

Приклади github.com/opavlyshak/dotNet-async-demos

@pavlyshak

pavlyshak@gmail.com