如何使HttpWebRequest异步

时间:2014-03-04 12:25:51

标签: c# httpwebrequest .net-4.5

我有这样的代码:

private async Task<string> Request(url)
    {
        Task<string> task = null;

        try
        {
            task = MakeAsyncRequest(url, "text/html");
            return await task;
        }

        catch
        {
            return null;
        }            
    } 

private async Task<string> MakeAsyncRequest(string url, string contentType)
    {
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
        request.ContentType = contentType;
        request.Method = WebRequestMethods.Http.Get;
        request.Timeout = 20000;
        request.Proxy = null;
        Task<WebResponse> task = Task.Factory.FromAsync(
        request.BeginGetResponse,
        asyncResult => request.EndGetResponse(asyncResult),
        (object)null);

            //issue here:
        return await task.ContinueWith(t => ReadStreamFromResponse(t.Result));
    }

private string ReadStreamFromResponse(WebResponse response)
    {
        using (Stream responseStream = response.GetResponseStream())
        using (StreamReader sr = new StreamReader(responseStream))
        {
            //Need to return this response 
            string strContent = sr.ReadToEnd();
            return strContent;
        }
    }

我在Request(url)循环

中呼叫foreach
foreach(var url in myUrlList)
{
  string body = Request(method).Result;
}

但由于某种原因,代码堆积在return await task.ContinueWith(t => ReadStreamFromResponse(t.Result));并且只是冻结。

有没有更好的方法可以做到这一点,或者有人可以解释发生了什么? 我对await ....

的错误没有任何错误

1 个答案:

答案 0 :(得分:7)

call to Result in your foreach loop is causing a deadlock,正如我在博客中解释的那样。总之,await将捕获“上下文”(例如,UI上下文),并使用它来恢复async方法。一些上下文(例如,UI上下文)仅允许上下文中的一个线程。因此,如果通过调用Result来阻止该特殊线程(例如,UI线程),则async方法无法在该上下文中继续执行。

因此,解决方案是更改foreach循环:

foreach(var url in myUrlList)
{
  string body = await ProcessAsync(method);
}

其他说明:

任务返回方法应以“异步”结尾,以跟随TAP guidelines

Task.Factory.FromAsync是不必要的; HttpWebRequest已经有了等待的方法。更好的选择是使用HttpClient代替。

我建议您不要使用Task.ContinueWith(或Task.ResultTask.Wait);请改用await

通过这些简化:

private async Task<string> MakeAsyncRequestAsync(string url, string contentType)
{
  HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
  request.ContentType = contentType;
  request.Method = WebRequestMethods.Http.Get;
  request.Timeout = 20000;
  request.Proxy = null;
  WebResponse response = await request.GetResponseAsync();
  return ReadStreamFromResponse(response);
}

如果您将HttpWebRequest更改为HttpClient,则可以进一步简化此代码。