如何按照最佳实践在C# 4中创建异步方法?

14
考虑以下代码片段:
public static Task<string> FetchAsync()
{
    string url = "http://www.example.com", message = "Hello World!";

    var request = (HttpWebRequest)WebRequest.Create(url);
    request.Method = WebRequestMethods.Http.Post;

    return Task.Factory.FromAsync<Stream>(request.BeginGetRequestStream, request.EndGetRequestStream, null)
        .ContinueWith(t =>
        {
            var stream = t.Result;
            var data = Encoding.ASCII.GetBytes(message);
            Task.Factory.FromAsync(stream.BeginWrite, stream.EndWrite, data, 0, data.Length, null, TaskCreationOptions.AttachedToParent)
                .ContinueWith(t2 => { stream.Close(); });
        })
        .ContinueWith<string>(t =>
        {
            var t1 =
                Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null)
                .ContinueWith<string>(t2 =>
                {
                    var response = (HttpWebResponse)t2.Result;
                    var stream = response.GetResponseStream();
                    var buffer = new byte[response.ContentLength > 0 ? response.ContentLength : 0x100000];
                    var t3 = Task<int>.Factory.FromAsync(stream.BeginRead, stream.EndRead, buffer, 0, buffer.Length, null, TaskCreationOptions.AttachedToParent)
                        .ContinueWith<string>(t4 =>
                        {
                            stream.Close();
                            response.Close();
                            if (t4.Result < buffer.Length)
                            {
                                Array.Resize(ref buffer, t4.Result);
                            }
                            return Encoding.ASCII.GetString(buffer);
                        });
                    t3.Wait();
                    return t3.Result;
                });
            t1.Wait();
            return t1.Result;
        });
}

它应该返回Task<string>,发送带有一些数据的HTTP POST请求,以字符串形式从web服务器返回结果,并尽可能高效。

  • 您在上面的示例中发现了任何异步流问题吗?
  • 在此示例中,在.ContinueWith()内使用.Wait()是否可以?
  • 除了异常处理之外,您是否看到此代码的其他问题?

17
你应该接受一些来自你其他问题的回答。 - Jimmy
你可能需要考虑将你的Task对象重命名,因为在.NET 4中已经有一个Task对象了。虽然你可以让它们一起工作,但是改变你的命名方式可能会更容易些。 - Erik Funkenbusch
神秘人,我没有任何自定义任务声明。我使用的任务类型来自.NET 4.0 BCL。 - Grief Coder
2个回答

3
如果异步相关的C# 4.0代码冗长丑陋,那么它很可能被正确地实现了。如果它简短明了,那么很可能没有被正确地实现;)
..不过,通过在WebRequest、Stream类上创建扩展方法并清理主方法,您可以使其看起来更有吸引力。 P.S.: 我希望C# 5.0的新async关键字和能够尽快发布。 参考资料: http://msdn.microsoft.com/en-us/vstudio/async.aspx

C# 5.0在PDC2010上的新功能:http://player.microsoftpdc.com/Session/1b127a7d-300e-4385-af8e-ac747fee677a - Konstantin Tarkus
5
或许你所说的“巨大且丑陋”是正确的,但我不明白这如何回答格里夫的任何具体问题。我很惊讶竟然被接受了。 - Matthew Flaschen
不确定为什么这被接受了,因为它实际上并没有回答原始问题。 - Odrade

0

你的想法是正确的,等待操作是不必要的 - 程序会一直阻塞直到结果准备好。


然而,更简单的方法是基于 ParallelExtensionsExtras 库中提供的示例。

他们为WebClient开发了扩展,可以完全满足您的需求:

static Task<string> FetchAsync()
{
    string url = "http://www.example.com", message = "Hello World!";

    return new WebClient().UploadStringTask(url, "POST", message);
}

您可以在.NET并行编程博客上的这篇文章中了解更多相关信息。


感谢您的输入。为什么您认为HttpWebRequest类型被标记为过时? - Grief Coder
2
HttpWebRequest没有被标记为过时,实际上WebClient也在使用它。也许你想到的是构造函数。那个确实已经过时了,因为你应该使用WebRequest.Create代替。 - Matthew Flaschen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接