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]

Async method builders and where the allocations go

 
 
  • Gérald Barré
The post on the compiler rewrite pulled an async method apart and found a generated struct with a MoveNext method. One field in that struct was left unexplained: public AsyncTaskMethodBuilder<int> <>t__builder; The builder is the other half of the machinery. The state machine knows where it is; the builder knows what to hand back to the caller and how to get running again. It is also the piece you can… [read more]