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

3

我正在进行一个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,但在普通的ASP.Net页面上也会出现相同的问题。本地主机上的服务localhost:13254工作正常。如果直接使用请求的GetResponseAsync()方法返回的任务,代码可以正常工作,但是如果将其包装在异步方法中,则会被阻塞。
有没有人知道代码有什么问题?:(我可以改用同步版本,但是异步函数在其他自托管服务中工作正常...所以如果可能的话,我想在这里也使用相同的代码。
2个回答

4

我在我的博客中描述了这种死锁行为,并在最近的MSDN文章中进行了描述

要解决这个问题,您可以在所有地方使用ConfigureAwait(false),或者使用同步方法。理想的解决方案是始终使用await而不使用WaitResult,但在您的情况下可能不可能(只有当Nancy与async委托一起工作时才会生效,即Get ["/async/"] = async p => { ... };)。


谢谢指出这个问题 :D 我还没有尝试过 Nancy Async,所以我得暂时忍受这个问题。 - Ruxo

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有所不同。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接