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

Invoking synchronous method asynchronously completes task faster than natural async methods

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

The ability to invoke a synchronous method asynchronously does nothing for scalability, because you’re typically still consuming the same amount of resources you would have if you’d invoked it synchronously (in fact, you’re using a bit more, since there’s overhead incurred to scheduling something ).

所以我想让我们试一试,我创建了使用 WebClientDownloadStringTaskAsyncDownloadString(同步)方法的演示应用程序。

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

  1. 下载HtmlNotAsyncInAsyncWay

    这为同步方法 DownloadString 提供了异步方法包装器,它不应该很好地扩展。

  2. 下载HTMLCSAsync

    这会调用异步方法 DownloadStringTaskAsync。

我用这两种方法创建了 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 link.

为什么第一次选项完成的时间比第二次少?

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

Task.WhenAll(tasks); returns 一个 Task 完成所有这些任务。
您不对该任务做任何事情,因此您不会等待任何事情完成。

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

in fact, you’re using a bit more, since there’s overhead incurred to scheduling something

即使您如 SLaks 所建议的那样正确地等待任务,也几乎不可能准确测量此开销。

您的测试正在下载网页,这需要网络访问。 您尝试测量的开销 soooo 比网络延迟的方差小得多,它会在噪音中丢失。