简体   繁体   English

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

[英]Chaining tasks of varying return types (TPL and HttpWebRequest)

Folks, I'm finding myself in a weird situation. 伙计们,我发现自己陷入了一种奇怪的境地。 I need to chain tasks of different returning types together. 我需要将不同返回类型的任务链接在一起。 Normally, you can do something like this 通常,你可以做这样的事情

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

My complication however, lies in using the FromAsync helper method to wrap a Begin/End pair and convert that into a Task. 然而,我的复杂性在于使用FromAsync辅助方法来包装Begin / End对并将其转换为Task。 I'm trying to write an asynchronous client using HttpWebRequest and tasks on .NET 4.0 (so await is not an option). 我正在尝试使用HttpWebRequest和.NET 4.0上的任务编写异步客户端(所以await不是一个选项)。

My problem is that the return type of FromAsync is a Task itself which prevents me from using it in a ContinueWith method ( ContinueWith expects the return type and wraps the data itself in a Task object). 我的问题是FromAsync的返回类型是一个Task本身,它阻止我在ContinueWith方法中使用它( ContinueWith期望返回类型并将数据本身包装在Task对象中)。

Here is the code I have so far, which yields the correct functional result, but is not truly asynchronous: 这是我到目前为止的代码,它产生了正确的功能结果,但不是真正的异步:

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

To rephrase your question, you have Task<Task<string>> and you want to get Task<string> from that, without synchronously waiting for the Task to complete. 要重新解释您的问题,您有Task<Task<string>>并且您希望从中获取Task<string> ,而无需同步等待Task完成。

In C# 5.0, you could do this by using double await : return await await task; 在C#5.0中,您可以通过使用double await来执行此操作: return await await task; .

Without C# 5.0, you can use Unwrap() , it does exactly what you want: return task.Unwrap(); 没有C#5.0,你可以使用Unwrap() ,它完全符合你的需要: return task.Unwrap(); .

If, for some reason, you wanted to do this by yourself, you could use ContinueWith() inside ContinueWith() together with TaskCompletionSource . 如果由于某种原因,你想自己做这件事,你可以在ContinueWith()使用ContinueWith()TaskCompletionSource


But your code is flawed: it assumes that you will get the whole response in a single read. 但是你的代码存在缺陷:它假设你将在一次读取中获得整个响应。 That's not guaranteed at all, and in fact won't work correctly quite often. 这根本不能保证,实际上不会经常正常工作。 Doing this properly would require more complicated code and probably also TaskCompletionSource . 正确地执行此操作将需要更复杂的代码,并且可能还需要TaskCompletionSource

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM