我正在开发一个Nancy / ASP.Net项目。我试图使用WebRequest从其他Web服务获取数据,并且我已经在异步函数中实现了请求。我发现一个问题,当我尝试等待函数返回的任务时,它会被无限期地阻塞。简化的代码看起来像这样..
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();
}
}
代码使用NancyFx,但它也发生在vanilla ASP.Net页面上。 localhost:13254上的服务工作正常。如果我使用从请求的GetResponseAsync()直接返回的任务,代码工作正常,但如果我将其包装在异步方法中,它就会被阻止。
有没有人知道代码有什么问题? :(我可以更改为使用同步版本,但异步功能可以在其他自托管服务中找到...所以我想在这里也使用相同的代码...
答案 0 :(得分:3)
我描述了这种死锁行为on my blog和in a recent MSDN article。
要解决此问题,您可以在任何地方使用ConfigureAwait(false)
,也可以使用同步方法。 理想解决方案是一直使用await
,永远不会使用Wait
或Result
,但在您的情况下可能无法实现(它只会起作用)如果Nancy与async
代表合作,即Get["/async/"] = async p => { ... };
)。
答案 1 :(得分:1)
除了Stephen提供的解决方案之外,我只需更改Nancy路由处理程序根目录下的同步上下文即可解决。鉴于此效用函数:
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);
}
}
}
}
用这个函数包装处理程序
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!";
});
它只是工作^^ a我认为我不需要ASP.Net上下文...实际上我希望Nancy能够创建自己的同步上下文,因为Nancy在概念上与Asp.net的工作方式不同。