How to make HttpWebRequest async

风格不统一 提交于 2019-12-23 19:00:07

问题


I have such code:

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;
        }
    }

I am calling Request(url) in foreach loop

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

But for some reason the code getting stacked at return await task.ContinueWith(t => ReadStreamFromResponse(t.Result)); and just freezing.

Is there a better way of doing this or may be some one can explain what is happening? I am not getting any errors just something wrong with the await....


回答1:


The call to Result in your foreach loop is causing a deadlock, as I explain on my blog. In summary, await will capture a "context" (e.g., a UI context), and use that to resume the async method. Some contexts (e.g., the UI context) only allow one thread in the context. So if you block that special thread (e.g., the UI thread) by calling Result, then the async method cannot resume execution within that context.

So, the solution is to change your foreach loop:

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

Other notes:

Task-returning methods should end in "Async" to follow the TAP guidelines.

Task.Factory.FromAsync is unnecessary; HttpWebRequest already has awaitable methods. An even better option is to use HttpClient instead.

I recommend that you not use Task.ContinueWith (or Task.Result, or Task.Wait); use await instead.

With these simplifications in place:

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);
}

This code could be simplified further if you change HttpWebRequest to HttpClient.



来源:https://stackoverflow.com/questions/22171928/how-to-make-httpwebrequest-async

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!