问题
I was having a problem with a hanging await (described here). During research I found out that calling SetResult
on my TaskCompletionSource
actually invokes awaiting continuation in the context of the thread that called SetResult
(this is also spelled out in this answer to a somewhat related question). In my case this is a different thread (a thread-pool worker thread) from the one that started the await (an ASP.NET request thread).
While I'm still not sure why this would cause a hang, I decided to try forcing the SetResult
into the original context. I stored the value of SynchronizationContext.Current
before entering await on the request thread and manually applied it in the worker thread via SynchronizationContext.SetSynchronizationContext
just before calling SetResult
. This solved the hang and I can now await all my async methods without having to specify ConfigureAwait(false)
.
My question is: is this a reasonable and correct approach to manually capturing and applying the SynchronizationContext
? FWIW, I tried doing a simple Post()
with the SetResult
delegate first, but that still caused a hang. I'm obviously a bit out of my comfort zone here... Please help me understand what's going on!
回答1:
SetResult
is not guaranteed to call anything. Therefore, this is not reliable.
You need to switch the sync context at the point where it is captured. A common pain point here is WebClient
which captures the context when starting a web request. So your code would look like this:
SetContext(newContext);
new WebClient().DownloadAsync(...);
SetContext(oldContext);
Restore the old context to not disturb anything.
In other words the problem is in the continuation code, not in the code calling SetResult
.
回答2:
To my embarrassment, I had completely overlooked that my HTTP handler was derived from a small base class, which implemented IAsyncHttpHandler
in a very questionable way in order to add support for async handlers:
public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
{
...
var task = HandleRequestAsync(...);
Task.Run(async () => { await task; }).GetAwaiter().GetResult();
...
}
I can't even remember why I did this in the first place (it was over a year ago), but it definitely was THE stupid part I was looking for for the last couple of days!
Changing the handler base class to .NET 4.6's HttpTaskAsyncHandler
got rid of the hangs. Sorry for wasting everyone's time! :(
来源:https://stackoverflow.com/questions/41011778/manually-capturing-and-applying-synchronizationcontext-when-completing-a-task