async-await best practices in 10 minutes

Post on 22-Aug-2015

860 Views

Category:

Documents

11 Downloads

Preview:

Click to see full reader

TRANSCRIPT

50ª Reunião Presencial @ LISBOADateTime.Parse(“22-11-2014", new CultureInfo("pt-PT"));

http://netponto.org

hashtag #netponto

Async-Await Best Practincesin 10 minutes

Paulo Morgado

http://netponto.org50ª Reunião Lisboa – 22-11-2014

Paulo Morgado• http://PauloMorgado.NET/• @PauloMorgado• http://about.me/PauloMorgado• http://www.slideshare.net/PauloJorgeMorgado• http://pontonetpt.org/blogs/paulomorgado/• http://blogs.msmvps.com/paulomorgado/• http://weblogs.asp.net/paulomorgado• http://www.revista-programar.info/author/pmorgad

o/

For goodness’ sake,stop using async void!

Async void is only for event handlersPrinciplesAsync 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 elseIf you need fire-and-forget elsewhere, indicate it explicitly e.g. “FredAsync().FireAndForget()”When you see an async lambda, verify it

The problem is events.They’re not going away.

Async over events• Principles

Callback-based programming, as with events, is hard

• GuidanceIf the event-handlers are largely independent, then leave them as eventsBut if they look like a state-machine, then await is sometimes easierTo turn events into awaitable Tasks, use TaskCompletionSource

Is it CPU-bound,or I/O-bound?

ThreadpoolPrinciplesCPU-bound work means things like: LINQ-over-objects, or big iterations, or computational inner loops.Parallel.ForEach and Task.Run are a good way to put CPU-bound work onto the thread pool.Thread pool will gradually feel out how many threads are needed to make best progress.Use of threads will never increase throughput on a machine that’s under load.

GuidanceFor IO-bound “work”, use await rather than background threads.For CPU-bound work, consider using background threads via Parallel.ForEach or Task.Run, unless you're writing a library, or scalable server-side code.

Don’t lie

Two ways of thinking about asynchrony

• Foo();• Perform something here and now.• I’ll regain control to execute

something else when it’s done.

• var task = FooAsync();• Initiate something here and now.• I’ll regain control to execute

something else “immediately”.

From the method signature (how people call it)

Uses a CPU core solidly while it runs

void Foo(){ for (int i=0; i<100; i++) Math.Sin(i);}

From the method implementation (what resources it uses) Hardly touches the CPU

async Task FooAsync(){ await client.DownloadAsync();}

Async methods: Your caller’s assumptions“This method’s name ends with ‘Async’, so…”

“…calling it won’t spawn new threads in my server app”

“…I can parallelize by simply calling it multiple times”

Is this true for your async methods?

Libraries generally shouldn’t use Task.Run()

Your callers should be the ones to call Task.Run“await task;”Captures the current SyncContext before awaiting.When it resumes, uses SyncContext.Post() to resume “in the same place”(If SyncContext is null, uses the TaskScheduler)

For application-level code:This behavior is almost always what you want.

For library-level code:This behavior is rarely what you want!

Sync methods: Your caller’s assumptions“There’s a synchronous version of this method…”

“…I guess it must be faster than the async version”

“…I can call it from the UI thread if the latency’s fine”

void Foo() { FooAsync().Wait(); } -- will deadlock!!!

Library methods shouldn't liePrinciplesIn a server app, spinning up threads hurts scalabilty.The app (not the library) is in the best position to manage its own threads.Users will assume they know your method's implementation by looking at its signature.

GuidanceDefine an async signature “FooAsync” when your implementation is truly async.Define a sync signature "Foo" when your implementation is fast and won't deadlock.Don't use blocking calls to Wait() .Result in libraries; that invites deadlocks.

Use ConfigureAwait(false)

SynchronizationContextRepresents a target for work via its Post methodWindowsFormsSynchronizationContext

.Post() does Control.BeginInvoke

DispatcherSynchronizationContext.Post() does Dispatcher.BeginInvoke

AspNetSynchronizationContext.Post() ensures one-at-a-time

… // ~10 in .NET Framework, and you can write your own… // Is the core way for “await” to know how to put you back

SynchronizationContext and AwaitPrinciplesIn a server app, spinning up threads hurts scalabilty.The app (not the library) is in the best position to manage its own threads.Users will assume they know your method's implementation by looking at its signature.

GuidanceDefine an async signature “FooAsync” when your implementation is truly async.Define a sync signature "Foo" when your implementation is fast and won't deadlock.Don't use blocking calls to Wait() .Result in libraries; that invites deadlocks.

SynchronizationContext: ConfigureAwaitTask.ConfigureAwait(bool continueOnCapturedContext)await t.ConfigureAwait(true) // default

Post continuation back to the current context/scheduler

await t.ConfigureAwait(false)If possible, continue executing where awaited task completes

ImplicationsPerformance (avoids unnecessary thread marshaling)Deadlock (code shouldn’t block UI thread, but avoids deadlocks if it does)

Use ConfigureAwait(false)PrinciplesSynchronizationContext is captured before an await, and used to resume from await.In a library, this is an unnecessary perf hit.It can also lead to deadlocks if the user (incorrectly) calls Wait() on your returned Task..

GuidanceIn library methods, use "await t.ConfigureAwait(false);"

Await all the way

Library perf considerationsPrinciplesThe compiler provides, through the await keyword, sequential execution of the code.

GuidanceDon’t mix async-await with ContinuesWith.

Task.Run is the way to create new tasks

Task.RunPrinciplesTask.Run returns hot tasks (running or completed) created with settings suited to async-await.

GuidanceDon’t use Task.Factory.StartNew or the Task (or Task<T>) constructor.

Use the CancellationToken

Use the CancellationTokenPrinciplesThe CancellationToken structure is the way to signal and handle cancellation.

GuidanceIf you want your API to be cancellable, use cancellation tokens.If your code uses APIs that use cancellation tokens, use them.Always check the cancellation tokens.

Library perf considerations

Library perf considerationsPrinciplesAsync methods are faster than what you could write manually, but still slower than synchronous.The chief cost is in memory allocation (actually, in garbage collection).The "fast path" bypasses some allocations.

GuidanceAvoid designing "chatty" APIs where async methods are called in an inner loop; make them "chunky".If necessary, cache the returned Task object (even with cache size "1"), for zero allocations per call.As always, don't prematurely optimize!

Questões?

Resources• Talk: Async best practices

– http://blogs.msdn.com/b/lucian/archive/2013/11/23/talk-mvp-summit-async-best-practices.aspx

• Six Essential Tips For Async – Introduction– http://channel9.msdn.com/Series/Three-Essential-Tips-for-Async/Three-Essential-Tips-

For-Async-Introduction

• Curah! async-await General– http://curah.microsoft.com/45553/asyncawait-general

• Curah! async-await and ASP.NET– http://curah.microsoft.com/44400/async-and-aspnet

Questões?

Patrocinadores “GOLD”

Twitter: @PTMicrosoft

http://www.microsoft.com/portugal

Twitter: @FindMoreC

http://www.findmore.eu

Patrocinadores “Bronze”

http://bit.ly/netponto-aval-50

* Para quem não puder preencher durante a reunião, iremos enviar um email com o link à tarde

Próximas reuniões presenciais22/11/2014 – Novembro – 50ª Reunião! (Lisboa)13/12/2014 – Dezembro (Lisboa)24/01/2015 – Janeiro (Lisboa)??/??/2015 – ????? (Porto)??/??/2015 – ????? (?????)

Reserva estes dias na agenda! :)

top related