【问题标题】:Chaining tasks of varying return types (TPL and HttpWebRequest)链接不同返回类型的任务(TPL 和 HttpWebRequest)
【发布时间】:2013-01-21 21:55:10
【问题描述】:

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

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

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

我的问题是 FromAsync 的返回类型是一个 Task 本身,它阻止我在 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;
}

【问题讨论】:

  • return Task.WhenAll(t, t2); Task.WhenAll 没有这样的过载。
  • @HamletHakobyan 当然可以,那是因为它是 params 方法(至少在 .Net 4.5 上,在 4.0 上,根本没有 WhenAll())。
  • 如果你使用VS2012和Microsoft.Bcl.Async,你可以在.Net 4.0上使用await

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


【解决方案1】:

重新表述你的问题,你有Task&lt;Task&lt;string&gt;&gt;,你想从中得到Task&lt;string&gt;,而不是同步等待Task完成。

在 C# 5.0 中,您可以通过使用 double await: return await await task; 来做到这一点。

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

如果出于某种原因,您想自己执行此操作,您可以在 ContinueWith() 中使用 ContinueWith()TaskCompletionSource


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

【讨论】:

  • 谢谢@svick。 Unwrap 正是我想要的。尽管按照您的建议,我引入了一个循环来正确读取整个响应,并且显着改变了代码的结构(我不再需要展开!)
猜你喜欢
  • 1970-01-01
  • 2020-12-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多