Demystifying Asynchronous Programming in .NET
What is asynchronous programming - Unit of work running separately from its caller. - Common implementations: promises and callbacks.
Asynchronous == Parallelism? Yes. No. Well, sort of. Parallelism can happen with the TPL although it’s not the goal. Parallelism -> splitting a task into smaller pieces to execute them concurrently.
Task Parallel Library (TPL) ● Includes asynchronous and parallelism APIs (Task, Parallel LINQ). ● Gotcha: although being in the .NET CLR, F# uses a completely distinct implementation. ● Mostly about Task and the underlying plumbing. ● async/await is the language mechanism to generate the code around Tasks for continuations
In C# async Task<String> GetContent(String url) { return await _httpClient.GetAsync(url); } C# 5 .NET 4.5
F# Async Distinct implementation, predates the TPL. let sleep milliseconds message: WebPart = fun (x : HttpContext) -> async { do! Async.Sleep milliseconds return! OK message x }
Non blocking I/O - Non blocking I/O is commonly implied in .NET when talking about async/await and the TPL. - Similar to NodeJs, same purpose to run many requests on few threads - Many of the .NET async APIs use async IO, such as ADO.NET
Non blocking I/O in Operating systems ● Known as overlapped I/O in Windows ● Posix AIO in Linux ● Socket AIO has arguably the most impact in web apps (database calls, HTTP, etc.)
Example: Dapper queries SqlConnection.Query() -> synchronous socket. Synchronous blocking SqlConnection.QueryAsync() -> asynchronous socket. Asynchrounous non blocking Task.Run(() => SqlConnection.Query() -> still a synchronous socket) Asynchrounous blocking. SqlConnection.QueryAsync().Result -> Asynchronous call and blocks the current thread
Why all the fuss about asynchronous anyway (Supposing asynchronous IO) ● Scalability, handling many requests in parallel ● Better handling of spikes. .NET throttle thread creation at 1 per half second. Async regardless of IO ● Freeing the UI thread in a desktop application ● Asynchronous flows
Async all the things
Not so fast - Need to propagate the async all the way up to the caller - Overhead on pure CPU code - Exception handling is tricky. - Not meant for fire and forget. Hard to debug, monitor, error handling.
You can block on async code. Don’t. Task.Result -> Congratulations, it’s a deadlock (probably) Blocking -> the async IO is now synchronous. Frameworks such as ASP.NET force a request to continue on the same thread. Doesn’t happen anymore in ASP.NET Core, no synchronization context.
State machine ● The async/await is replaced with a state machine by the compiler ● Async/await is a language construct -> doesn’t exist in IL. ● Could be done manually, lot of boilerplate
Quiz var tasks = Enumerable.Range(0, 100).Select(async _ => await Task.Delay(1000)).ToArray(); var tasks = Enumerable.Range(0, 100).Select(async _ => await Task.Run(() => Thread.Sleep(1000))).ToArray(); Task.WaitAll(tasks);
async _ => await Task.Delay(1000)
async _ => await Task.Run(() => Thread.Sleep(1000))
Beware of background processing Easy to fire and forget -> doesn’t mean you should - Error handling is thrown out the window - No priority on what is executed - No way of cancelling anything - Hell to monitor/debug - Accessing thread scoped dependencies and all hell breaks loose. - Hangfire is a worthy alternative.
Cancellation ● Cancellation token to cancel a task ● Essentially throws an exception, bubbling up the call chain ● Useful for cleanup of resources or stop intensive processing var tokenSource = new CancellationTokenSource(); var task = _httpClient..GetAsync("http://www.google.com", cts.Token); tokenSource.Cancel();
Takeaways ● Non-blocking I/O is usually implied with async in .NET ● Asynchronous programming has many benefits but shouldn’t be used for all methods. ● Blocking on async code essentially nullifies all gain, if you’re lucky. Use await! ● If you’re not lucky, deadlock
Questions? Twitter: @plmaheu

Asynchronous Programming in .NET

  • 1.
  • 2.
    What is asynchronousprogramming - Unit of work running separately from its caller. - Common implementations: promises and callbacks.
  • 3.
    Asynchronous == Parallelism? Yes.No. Well, sort of. Parallelism can happen with the TPL although it’s not the goal. Parallelism -> splitting a task into smaller pieces to execute them concurrently.
  • 4.
    Task Parallel Library(TPL) ● Includes asynchronous and parallelism APIs (Task, Parallel LINQ). ● Gotcha: although being in the .NET CLR, F# uses a completely distinct implementation. ● Mostly about Task and the underlying plumbing. ● async/await is the language mechanism to generate the code around Tasks for continuations
  • 5.
    In C# async Task<String>GetContent(String url) { return await _httpClient.GetAsync(url); } C# 5 .NET 4.5
  • 6.
    F# Async Distinct implementation,predates the TPL. let sleep milliseconds message: WebPart = fun (x : HttpContext) -> async { do! Async.Sleep milliseconds return! OK message x }
  • 7.
    Non blocking I/O -Non blocking I/O is commonly implied in .NET when talking about async/await and the TPL. - Similar to NodeJs, same purpose to run many requests on few threads - Many of the .NET async APIs use async IO, such as ADO.NET
  • 8.
    Non blocking I/Oin Operating systems ● Known as overlapped I/O in Windows ● Posix AIO in Linux ● Socket AIO has arguably the most impact in web apps (database calls, HTTP, etc.)
  • 9.
    Example: Dapper queries SqlConnection.Query()-> synchronous socket. Synchronous blocking SqlConnection.QueryAsync() -> asynchronous socket. Asynchrounous non blocking Task.Run(() => SqlConnection.Query() -> still a synchronous socket) Asynchrounous blocking. SqlConnection.QueryAsync().Result -> Asynchronous call and blocks the current thread
  • 10.
    Why all thefuss about asynchronous anyway (Supposing asynchronous IO) ● Scalability, handling many requests in parallel ● Better handling of spikes. .NET throttle thread creation at 1 per half second. Async regardless of IO ● Freeing the UI thread in a desktop application ● Asynchronous flows
  • 11.
  • 12.
    Not so fast -Need to propagate the async all the way up to the caller - Overhead on pure CPU code - Exception handling is tricky. - Not meant for fire and forget. Hard to debug, monitor, error handling.
  • 13.
    You can blockon async code. Don’t. Task.Result -> Congratulations, it’s a deadlock (probably) Blocking -> the async IO is now synchronous. Frameworks such as ASP.NET force a request to continue on the same thread. Doesn’t happen anymore in ASP.NET Core, no synchronization context.
  • 14.
    State machine ● Theasync/await is replaced with a state machine by the compiler ● Async/await is a language construct -> doesn’t exist in IL. ● Could be done manually, lot of boilerplate
  • 15.
    Quiz var tasks =Enumerable.Range(0, 100).Select(async _ => await Task.Delay(1000)).ToArray(); var tasks = Enumerable.Range(0, 100).Select(async _ => await Task.Run(() => Thread.Sleep(1000))).ToArray(); Task.WaitAll(tasks);
  • 16.
    async _ =>await Task.Delay(1000)
  • 17.
    async _ =>await Task.Run(() => Thread.Sleep(1000))
  • 18.
    Beware of backgroundprocessing Easy to fire and forget -> doesn’t mean you should - Error handling is thrown out the window - No priority on what is executed - No way of cancelling anything - Hell to monitor/debug - Accessing thread scoped dependencies and all hell breaks loose. - Hangfire is a worthy alternative.
  • 19.
    Cancellation ● Cancellation tokento cancel a task ● Essentially throws an exception, bubbling up the call chain ● Useful for cleanup of resources or stop intensive processing var tokenSource = new CancellationTokenSource(); var task = _httpClient..GetAsync("http://www.google.com", cts.Token); tokenSource.Cancel();
  • 20.
    Takeaways ● Non-blocking I/Ois usually implied with async in .NET ● Asynchronous programming has many benefits but shouldn’t be used for all methods. ● Blocking on async code essentially nullifies all gain, if you’re lucky. Use await! ● If you’re not lucky, deadlock
  • 21.

Editor's Notes

  • #3 Sending an email and doing something else while waiting for an answer.
  • #14 Code that works in a console application but deadlocks in an ASP.NET application SynchronizationContext -> https://blog.stephencleary.com/2017/03/aspnetcore-synchronization-context.html
  • #15 https://weblogs.asp.net/dixin/understanding-c-sharp-async-await-1-compilation
  • #16 Around a second for Task.Delay Around 12 seconds Task.Run
  • #19 The list goes on. At the very have an executor class so you can refactor later without having to hunt for all Task.Run calls. Memes: Jurassic Park, because you could doesn’t mean you should Friends don’t let each other Task.Run()
  • #20 Cancellation token