为什么等待从异步方法返回的任务被阻止?

时间:2013-06-08 16:54:09

标签: c# asynchronous async-await nancy

我正在开发一个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()直接返回的任务,代码工作正常,但如果我将其包装在异步方法中,它就会被阻止。

有没有人知道代码有什么问题? :(我可以更改为使用同步版本,但异步功能可以在其他自托管服务中找到...所以我想在这里也使用相同的代码...

2 个答案:

答案 0 :(得分:3)

我描述了这种死锁行为on my blogin a recent MSDN article

要解决此问题,您可以在任何地方使用ConfigureAwait(false),也可以使用同步方法。 理想解决方案是一直使用await,永远不会使用WaitResult,但在您的情况下可能无法实现(它只会起作用)如果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的工作方式不同。