The cost of async: a BenchmarkDotNet study on .NET 10

 
 
  • Gérald Barré
This series has measured one thing at a time. This post puts the numbers together, because the interesting question is not "what does an await cost" but "when does any of it show up in my application". It is also the last post before .NET 11, where the runtime takes over and several of these numbers change. The two paths Everything reduces to a single distinction. An async method either completes without… [read more]

Prefer async/await over directly returning Task

 
 
  • Gérald Barré
These two methods look equivalent: public Task<int> DoSomethingAsync() { return CallDependencyAsync(); } public async Task<int> DoSomethingAsync() { return await CallDependencyAsync(); } The first is called eliding the await. It is faster, and it is usually the wrong choice. This post is about what you actually trade. What eliding saves Without async, the method is an ordinary method that returns a value… [read more]

Synchronization in async code: lock and SemaphoreSlim

 
 
  • Gérald Barré
Once operations run concurrently, they start touching the same state. The synchronous answer is lock, and it does not work here: lock (_gate) { await LoadAsync(); // CS1996: Cannot await in the body of a lock statement } That is a compiler error, and it is protecting you from something worse than a compiler error. Why the compiler refuses lock compiles to Monitor.Enter and Monitor.Exit, and Monitor is… [read more]

Composing async work: WhenAll WhenAny and WhenEach

 
 
  • Gérald Barré
Everything so far has been about a single operation. This post is about running several, which is where async stops being a syntax feature and starts changing what your program does. The mechanics are three combinators and one throttle, and each has an exception behaviour that is worth knowing before you meet it in production. Starting is what makes them concurrent Before the combinators, the thing they… [read more]

SynchronizationContext ConfigureAwait and deadlocks

 
 
  • Gérald Barré
The previous post covered ExecutionContext, which decides what state a continuation sees. This post is about the other thing await captures, which decides where the continuation runs. It is also the mechanism behind the single most famous bug in async C#: calling .Result and hanging forever. What a SynchronizationContext is A SynchronizationContext is an abstraction over "post this callback to the right… [read more]