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....

有帮助吗?

解决方案

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.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top