Http POST使用Task.FromAsync与异步请求和异步响应

时间:2013-11-06 04:52:25

标签: c# asynchronous task-parallel-library

我必须向Web服务发送异步POST,并且我想异步发送/检索数据。

我还需要发送请求,但只等待最多1500毫秒等待响应。如果我没有收到回复,程序应该继续(这是一个服务进行外部Web服务调用)。我想将这些服务调用卸载到IOCP,而不是长时间阻塞并等待它们返回。我只想阻止1500毫秒。

这是我到目前为止所拥有的:

 var httpRequest = (HttpWebRequest)WebRequest.Create(@"urltoPostTo");

            httpRequest.Method = "POST";
            byte[] data = Encoding.ASCII.GetBytes("test-post");
            httpRequest.ContentLength = data.Length;
            var asyncTask = Task.Factory.FromAsync<Stream>(httpRequest.BeginGetRequestStream, httpRequest.EndGetRequestStream, httpRequest)
                .ContinueWith(response =>
                {
                    var localStream = response.Result;
                    if (localStream != null)
                    {
                        localStream.Write(data, 0, data.Length);
                        localStream.Close();
                    }
                });  //how do I do the continuation for BeginGetResponse and EndGetResponse from here?

我有一些要求,遗憾的是我无法改变。

  1. 我使用的是针对4.0的Visual Studio 2010
  2. 我无法使用Async BCL
  3. 我想尝试使用Task.FromAsync

3 个答案:

答案 0 :(得分:1)

Implementing extension method WebRequest.GetResponseAsync with support for CancellationToken已经使用GetResponseAsync扩展方法正确处理了超时和CancellationTokens。当Request.Timeout到期时,该方法在将Task返回到已取消状态之前调用Request.Abort

这种涉及编码的原因是客户(你的)有责任正确处理超时,因此你不能依赖FromAsync来处理超时过期。也许这就是为什么FromAsync不接受取消令牌的原因。

另一个选择是避免取消请求本身并取消继续。您可以使用接受CancellationToken的ContinueWith重载并调用CancellationTokenSource.CancelAfter来设置取消超时。

这将允许您的代码忽略结果并继续运行,但它不会破坏与服务器的连接,也不会阻止后台线程处理任何可能昂贵的结果。

你可以这样写:

var tcs=new CancellationTokenSource();
var asyncTask = Task.Factory.FromAsync<Stream>(httpRequest.BeginGetRequestStream, httpRequest.EndGetRequestStream, httpRequest)
.ContinueWith(response =>
{...},
cts.Token);  
cts.CancelAfter(1500);

请注意,在启动异步任务后,将完成对CancelAfter的调用。

我更喜欢Reed Copsey在繁忙站点中的扩展方法,因为大量已取消但未完成的请求很容易耗尽线程池,无理由消耗大量内存并消耗可能昂贵的外部系统连接。

答案 1 :(得分:0)

为什么不使用HttpClient?

webApiHttpClient.PostAsJsonAsync(GetFullAPI("api/Subscribe"), obj)
            .ContinueWith(res => _logger.InfoFormat("Subscribe result: {0}", res.Result.StatusCode));

答案 2 :(得分:0)

尝试使用此方法帮助程序。

public static Task<string> Post(string url, Encoding encoding, string content)
            {
                var httpRequest = (HttpWebRequest)WebRequest.Create(url);
                httpRequest.Method = "POST";
                byte[] data = encoding.GetBytes(content);
                httpRequest.ContentLength = data.Length;

                TaskCompletionSource<string> result = new TaskCompletionSource<string>();


                Task.Factory.FromAsync<Stream>(httpRequest.BeginGetRequestStream, httpRequest.EndGetRequestStream, httpRequest)
                   .ContinueWith(requestStreamTask =>
                   {
                       try
                       {
                           using (var localStream = requestStreamTask.Result)
                           {
                               localStream.Write(data, 0, data.Length);
                               localStream.Flush();
                           }

                           Task.Factory.FromAsync<WebResponse>(httpRequest.BeginGetResponse, httpRequest.EndGetResponse, httpRequest)
                               .ContinueWith(responseTask =>
                               {
                                   try
                                   {
                                       using (var webResponse = responseTask.Result)
                                       using (var responseStream = webResponse.GetResponseStream())
                                       using (var sr = new StreamReader(responseStream, encoding))
                                       {
                                           result.SetResult(sr.ReadToEnd());
                                       }
                                   }
                                   catch (Exception e)
                                   {
                                       result.SetException(e);
                                   }
                               }, TaskContinuationOptions.AttachedToParent);
                       }
                       catch (Exception e)
                       {
                           result.SetException(e);
                       }

                   }, TaskContinuationOptions.AttachedToParent);

                return result.Task;
            }