Reputation: 45252
I created an ASP WebApplication using Visual Studio 2012.
If I modify the default page as follows:
public partial class _Default : Page
{
static async Task PerformSleepingTask()
{
Action action = () =>
{
Thread.Sleep(TimeSpan.FromSeconds(0.5));
int dummy = 3; // Just a nice place to put a break point
};
await Task.Run(action);
}
protected void Page_Load(object sender, EventArgs e)
{
Task performSleepingTask = PerformSleepingTask();
performSleepingTask.Wait();
}
}
On the call to performSleepingTask.Wait()
it hangs indefinitely.
<appSettings>
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="false" />
</appSettings>
Then it does work. The Wait
function waits for the sleeping to finish on a different thread, then continues.
Can somebody explain:
TaskFriendlySynchronizationContext
? (Given that it causes tasks to hang, I wouldn't call it "friendly")async
methods from page handler methods?Here's an implementation I came up with which works, but it feels like clumsy code:
protected void Page_Load(object sender, EventArgs e)
{
ManualResetEvent mre = new ManualResetEvent(false);
Action act = () =>
{
Task performSleepingTask = PerformSleepingTask();
performSleepingTask.Wait();
mre.Set();
};
act.BeginInvoke(null, null);
mre.WaitOne(TimeSpan.FromSeconds(1.0));
}
Upvotes: 2
Views: 667
Reputation: 456527
Why does it hang?
The Task
representing PerformSleepingTask
is trying to resume after its await
so that PerformSleepingTask
can return. It is trying to re-enter the ASP.NET request context, which is blocked by the call to Wait
. This causes a deadlock, as I expound on my blog.
To avoid the deadlock, follow these best practices:
async
all the way down. Don't block on async
code.ConfigureAwait(false)
in your "library" methods.Why do they have something called
TaskFriendlySynchronizationContext
? (Given that it causes tasks to hang, I wouldn't call it "friendly")
TaskFriendlySynchronizationContext
uses the new AspNetSynchronizationContext
(the .NET 4.0 TaskFriendlySynchronizationContext
has been renamed LegacyTaskFriendlySynchronizationContext
), and it also uses a new, async
-aware pipeline.
I'm not 100% sure about this one, but I suspect that the reason Page_Load
works for the legacy SyncCtx is that the old pipeline wasn't putting the SyncCtx in place yet. I'm not sure why it would act this way, though (unless Page.Async
was false
).
Is there a "best practice" for invoking async methods from page handler methods?
Absolutely. You can either just make your event handlers async void
, or use RegisterAsyncTask(new PageAsyncTask(...));
. The first approach is easier but the second approach is favored by the ASP.NET team.
Upvotes: 4