问题
I'm working on a Nancy/ASP.Net project. I've tried to use WebRequest to get data from other web service and I've implemented the request in an async function. I find a problem that when I try to wait on a task returned from the function, it gets blocked indefinitely. The simplified code looks like this..
using System.Net;
using System.Threading.Tasks;
using Nancy;
public class TestModule : NancyModule{
public TestModule() {
Get["/"] = p => "Hello, world";
Get["/async/"] = p => {
var req = WebRequest.CreateHttp("http://localhost:13254/");
// var responseTask = req.GetResponseAsync(); // this works!
var responseTask = getResponse(req); // this gets blocked!
var waitSuccess = responseTask.Wait(10000);
return waitSuccess ? "Yeah!" : "woooh!";
};
}
async Task<HttpWebResponse> getResponse(HttpWebRequest request) {
return (HttpWebResponse) await request.GetResponseAsync();
}
}
The code uses NancyFx but it happens as well on vanilla ASP.Net page. The service on localhost:13254 is working fine. If I use the task directly returned from request's GetResponseAsync(), the code works fine, but If I wrap it in an async method, it just gets blocked.
Does anyone have any idea what's wrong with the code? :( I can change to use synchronous version but the async function works find in other self-hosting services... so I'd like to use the same code here too if possible..
回答1:
I describe this deadlock behavior on my blog and in a recent MSDN article.
To fix this, you can either use ConfigureAwait(false)
everywhere, or you can use synchronous methods. The ideal solution is to use await
all the way and never use Wait
or Result
, but that may not be possible in your situation (it would only work if Nancy worked with async
delegates, i.e., Get["/async/"] = async p => { ... };
).
回答2:
In addition to solutions provided from Stephen, I resolve with just changing synchronization context at the root of Nancy route handler. Given this utility function:
using System;
using System.Threading;
namespace Utility.Async{
static public class TPContext{
static public T Call<T>(Func<T> handler) {
var currentContext = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(null);
try {
return handler();
}finally {
SynchronizationContext.SetSynchronizationContext(currentContext);
}
}
}
}
Just wrap the handler with this function
Get["/async/"] = p => TPContext.Call(() => {
var req = WebRequest.CreateHttp("http://localhost:13254/");
var responseTask = getResponse(req);
var waitSuccess = responseTask.Wait(10000);
return waitSuccess ? "Yeah!" : "woooh!";
});
and it just works ^^a I don't think I need ASP.Net context.. and actually I hope Nancy'd make its own synchronization context since Nancy conceptually works differently from Asp.net already.
来源:https://stackoverflow.com/questions/17001761/why-waiting-a-task-returned-from-async-method-gets-blocked