异步调用同步方法比自然异步方法更快地完成任务

时间:2015-05-05 14:10:34

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

抱歉标题不好。我目前正在学习TPL并阅读this博客文章,其中说明了

  

异步调用同步方法的能力对可伸缩性没有任何作用,因为如果你同步调用它,你通常仍会消耗相同数量的资源(事实上,你使用的更多,因为安排某些事情会产生开销。

所以我想让我们尝试一下,然后我创建了使用WebClient DownloadStringTaskAsyncDownloadString(同步)方法的演示应用程序。

我的演示应用程序有两种方法

  1. DownloadHtmlNotAsyncInAsyncWay

    这提供了围绕同步方法DownloadString的异步方法包装器,它不应该扩展良好。

  2. DownloadHTMLCSAsync

    这会调用异步方法DownloadStringTaskAsync。

  3. 我从两种方法创建了100个任务,并比较了消耗的时间,发现选项1比第二个消耗的时间少。为什么呢?

    这是我的代码。

    using System;
    using System.Diagnostics;
    using System.Net;
    using System.Threading.Tasks;
    
    public class Program
    {
        public static void Main()
        {
            const int repeattime = 100;
            var s = new Sample();
            var sw = new Stopwatch();
            var tasks = new Task<string>[repeattime];
            sw.Start();
            for (var i = 0; i < repeattime; i++)
            {
                tasks[i] = s.DownloadHtmlNotAsyncInAsyncWay();
            }
    
            Task.WhenAll(tasks);
            Console.WriteLine("==========Time elapsed(non natural async): " + sw.Elapsed + "==========");
            sw.Reset();
            sw.Start();
            for (var i = 0; i < repeattime; i++)
            {
                tasks[i] = s.DownloadHTMLCSAsync();
            }
    
            Task.WhenAll(tasks);
            Console.WriteLine("==========Time elapsed(natural async)    : " + sw.Elapsed + "==========");
            sw.Reset();
        }
    }
    
    public class Sample
        {
            private const string Url = "https://www.google.co.in";
    
            public async Task<string> DownloadHtmlNotAsyncInAsyncWay()
            {
                return await Task.Run(() => DownloadHTML());
            }
    
            public async Task<string> DownloadHTMLCSAsync()
            {
                using (var w = new WebClient())
                {
                    var content = await w.DownloadStringTaskAsync(new Uri(Url));
                    return GetWebTitle(content);
                }
            }
    
            private string DownloadHTML()
            {
                using (var w = new WebClient())
                {
                    var content = w.DownloadString(new Uri(Url));
                    return GetWebTitle(content);
                }
            }
    
            private static string GetWebTitle(string content)
            {
                int titleStart = content.IndexOf("<title>", StringComparison.InvariantCultureIgnoreCase);
                if (titleStart < 0)
                {
                    return null;
                }
                int titleBodyStart = titleStart + "<title>".Length;
                int titleBodyEnd = content.IndexOf("</title>", titleBodyStart, StringComparison.InvariantCultureIgnoreCase);
                return content.Substring(titleBodyStart, titleBodyEnd - titleBodyStart);
            }
        }
    

    Here是dotnetfiddle链接。

    为什么第一个选项在比第二次更短的时间内完成?

2 个答案:

答案 0 :(得分:8)

你实际上并没有测量任何东西。

Task.WhenAll(tasks);返回完成所有这些任务的Task 你没有做任何事情,所以你不等任何事情要完成。

因此,您只需测量每个备选方案的同步初始化。 Task.Run()只是将一个委托排队到线程池;它比设置HTTP请求的工作少。

答案 1 :(得分:0)

  

事实上,你使用的更多,因为安排某些事情会产生开销

正如SLaks所建议的那样,即使您正确等待任务,也几乎无法准确衡量这一开销。

您的测试正在下载需要网络访问权限的网页。 您尝试测量的开销 soooo 远小于网络延迟的差异,它会在噪声中丢失。