链接不同返回类型的任务(TPL和HttpWebRequest)

时间:2013-01-21 06:33:00

标签: c# task-parallel-library async-await

伙计们,我发现自己陷入了一种奇怪的境地。我需要将不同返回类型的任务链接在一起。通常,你可以做这样的事情

Task<T> t = Task<T>.Factory.StartNew(() => ... some T instance);
Task t2 = t.ContinueWith<U>(parent => ...);
return Task.WhenAll(t, t2);

然而,我的复杂性在于使用FromAsync辅助方法来包装Begin / End对并将其转换为Task。我正在尝试使用HttpWebRequest和.NET 4.0上的任务编写异步客户端(所以await不是一个选项)。

我的问题是FromAsync的返回类型是一个任务本身阻止我在ContinueWith方法中使用它(ContinueWith期望返回类型并将数据包装在其中一个Task对象)。

这是我到目前为止的代码,它产生了正确的功能结果,但不是真正的异步:

public Task<string> GetHttpRequest(string url, string contentType)
{
    var httpWebRequest = CreateHttpWebRequest(url, "GET", contentType);
    Task<WebResponse> httpTask = Task.Factory.FromAsync<WebResponse>(httpWebRequest.BeginGetResponse, httpWebRequest.EndGetResponse, null);

    return httpTask.ContinueWith(httpAntecedent =>
          {
              WebResponse webResponse = httpAntecedent.Result;
              Stream responseStream = webResponse.GetResponseStream();
              byte[] data = new byte[webResponse.ContentLength];

              var streamReadTask = Task<int>.Factory.FromAsync(responseStream.BeginRead, responseStream.EndRead, data, 0, data.Length, TaskCreationOptions.AttachedToParent);

              return streamReadTask.ContinueWith(parent =>
                  {
                      responseStream.Close();
                      webResponse.Close();

                      return Encoding.UTF8.GetString(data);
                  });
          }).Result;
}

1 个答案:

答案 0 :(得分:3)

要重新解释您的问题,您需要Task<Task<string>>,并且希望从中获得Task<string>,而无需同步等待Task完成。

在C#5.0中,您可以使用双awaitreturn await await task;来完成此操作。

如果没有C#5.0,您可以使用Unwrap(),它会完全符合您的要求:return task.Unwrap();

如果出于某种原因,您希望自己执行此操作,则可以ContinueWith()ContinueWith()一起使用TaskCompletionSource


但是你的代码有缺陷:它假设你将在一次读取中得到整个响应。这根本不能保证,实际上不会经常正常工作。正确地执行此操作需要更复杂的代码,可能还需要TaskCompletionSource