I have an async
method I am using to offload a few seconds' worth of fire-and-forget work so as not to slow down my page load. This work needs a bit of general setup and tidy-up; I want the (fast) setup to throw synchronously if it throws, but I don't want to force the tidy-up to run in the ASP context so I am using ConfigureAwait
on the bit I am awaiting:
public Task FireAndForget()
{
DoSetup();
return FireAndForgetAfterSetup();
}
private async Task FireAndForgetAfterSetup()
{
await AFewSecondsWorthOfWork().ConfigureAwait(false);
DoTidyUp();
}
protected void btn_Click(object sender, EventArgs e)
{
FireAndForget();
}
This seems odd, because
FireAndForgetAfterSetup
shouldn't really care whether or not it is being called from an ASP context, so why should it have to be the one callingConfigureAwait
?- If I change my mind and decide that
btn_Click
should wait forFireAndForget
to complete, has it already thrown away the ASP context(?)
Can someone explain to me if I'm misunderstanding?
I'm not sure why he didn't post it, but my exact two questions are answered in this blog post by Stephen Cleary:
ConfigureAwait(false)
in library methods that know they won't use the context, because...async
method has thrown away the UI context, the calling method still has its own copy. Thus the calling method couldawait
the library method and resume in the UI context... but it's going to deadlock when that happens.If your scenario is how to execute some (relatively) long-running task during load, ASP.NET allows this scenario through the Page.RegisterAsyncTask method. Scott Hansleman describes how to use it in The Magic of using Asynchronous Methods in ASP.NET 4.5 plus an important gotcha
Essentially, you create an asynchronous method that returns Task and call:
then call Page.ExecuteRegisteredAsyncTasks to start executing all registered tasks.
Scott Hanselman does a good job (of course) of describing why using an event handler, Task or background thread is a bad idea.
This is also described in "What Not to do in ASP.NET, What to do instead" in the "Asynchronous Page Events" section
The ASP.NET synchronization context doesn't allow fire-and-forget work items to be kicked off from within a request. The runtime actively monitors such things and will try to generate an exception since these code patterns lead to null refs, deadlocks, AVs, and other nastiness.
If you absolutely need to kick off fire-and-forget work in ASP.NET, consider using WebBackgrounder. It integrates with the ASP.NET extensibility points that are designed to allow for this. So it won't block the active request, but keep in mind Stephen's caveat: it's not ever guaranteed to be executed at all. If you require guaranteed execution, consider a reliability mechanism like Service Bus.