I'm currently writing a lot of async
library code, and I'm aware of the practice of adding ConfigureAwait(false)
after each async call so as to avoid marshalling the continuation code back to the original (usually UI) thread context. As I don't like the unlabelled Boolean parameter, I tended to write this as ConfigureAwait(continueOnCapturedContext: false)
instead.
I added an extension method to make it a little more readable (and reduce the typing somewhat):
public static class TaskExtensions
{
public static ConfiguredTaskAwaitable<TResult> WithoutCapturingContext<TResult>(this Task<TResult> task)
{
return task.ConfigureAwait(continueOnCapturedContext: false);
}
public static ConfiguredTaskAwaitable WithoutCapturingContext(this Task task)
{
return task.ConfigureAwait(continueOnCapturedContext: false);
}
}
So now I can have something like await SomethingAsync().WithoutCapturingContext()
instead of await SomethingAsync().ConfigureAwait(continueOnCapturedContext: false)
. I consider it an improvement, however even this starts to grate when I have to call a number of async
methods in the same block of code, as I end up with something similar to this:
await FooAsync().WithoutCapturingContext();
var bar = await BarAsync().WithoutCapturingContext();
await MoreFooAsync().WithoutCapturingContext();
var moreBar = await MoreBarAsync().WithoutCapturingContext();
// etc, etc
In my opinion it's starting to make the code a lot less readable.
My question was basically this: is there a way of reducing this down further (other than shortening the name of the extension method)?
There is no global setting to prevent the tasks within the method from capturing the synchronization context, but what you can do is change the synchronization context for just the scope of that method. In your specific case you could change the context to the default sync context for just the scope of that method.
It's easy enough to write a simple disposable class that changes the sync context and then changes it back when disposed:
public class SyncrhonizationContextChange : IDisposable
{
private SynchronizationContext previous;
public SyncrhonizationContextChange(SynchronizationContext newContext = null)
{
previous = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(newContext);
}
public void Dispose()
{
SynchronizationContext.SetSynchronizationContext(previous);
}
}
allowing you to write:
using(var change = new SyncrhonizationContextChange())
{
await FooAsync();
var bar = await BarAsync();
await MoreFooAsync();
var moreBar = await MoreBarAsync();
}
(Note setting the context to null
means it'll use the default context.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With