多线程应用程序中的async HttpWebRequest vs sync

时间:2014-12-08 20:25:26

标签: c# multithreading asynchronous httpwebrequest httpwebresponse

我有一个应用程序,我需要一次制作很多httpWebRequest,并尽可能快地获得每个响应。 Actualy我不需要快速获得所有响应,我需要尽可能快地得到每个响应。我的意思是,我发送每个请求之间必须有最短的时间。

我阅读了很多关于async / await技术的文章并选择了这个解决方案:

    private static void Main()
    {
        ServicePointManager.DefaultConnectionLimit = 100;
        ServicePointManager.Expect100Continue = false;
        ServicePointManager.UseNagleAlgorithm = false;

        ProcessAsync("https://www.google.ru");

        Console.ReadKey();
    }

    private static void ProcessAsync(string url)
    {
        for (var i = 1; i <= 100; i++)
            Task.Factory.StartNew(async () => await ProcessInternalAsync(url));
    }

    private static async Task ProcessInternalAsync(string url)
    {
        var request = GetRequest(url);

        var sw = Stopwatch.StartNew();
        await GetStringContentAsync(request);
        sw.Stop();
        Console.WriteLine(sw.ElapsedMilliseconds);
    }

    public static async Task<string> GetStringContentAsync(HttpWebRequest webRequest)
    {
        using (var response = (HttpWebResponse) await webRequest.GetResponseAsync()
                                                                .ConfigureAwait(false))
        {
            var content = await GetStringContentFromResponseAsync(response)
                .ConfigureAwait(false);
            return content;
        }
    }

    private static async Task<string> GetStringContentFromResponseAsync(HttpWebResponse response)
    {
        using (var responseStream = GetStreamForResponse(response))
        {
            if (responseStream == null)
                return null;
            using (var streamReader = new StreamReader(responseStream))
            {
                var content = await streamReader.ReadToEndAsync()
                                                .ConfigureAwait(false);
                return content;
            }
        }
    }    

这有效,但很慢......(每个请求500-1600毫秒)

所以我尝试了没有异步/等待的“简单”解决方案:

    private static void Main()
    {
        ServicePointManager.DefaultConnectionLimit = 100;
        ServicePointManager.Expect100Continue = false;
        ServicePointManager.UseNagleAlgorithm = false;

        Process("https://www.google.ru");

        Console.ReadKey();
    }

    private static void Process(string url)
    {
        for (var i = 1; i <= 100; i++)
            Task.Factory.StartNew(() => ProcessInternal(url));
    }

    private static void ProcessInternal(string url)
    {
        var request = GetRequest(url);

        var sw = Stopwatch.StartNew();
        GetStringContent(request);
        sw.Stop();
        Console.WriteLine(sw.ElapsedMilliseconds);
    }

    public static string GetStringContent(HttpWebRequest webRequest)
    {
        using (var response = (HttpWebResponse) webRequest.GetResponse())
        {
            var content = GetStringContentFromResponse(response);
            return content;
        }
    }

    private static string GetStringContentFromResponse(HttpWebResponse response)
    {
        using (var responseStream = GetStreamForResponse(response))
        {
            if (responseStream == null)
                return null;
            using (var streamReader = new StreamReader(responseStream))
            {
                var content = streamReader.ReadToEnd();
                return content;
            }
        }
    }    

它的工作速度更快! (每个请求55-180毫秒)。

以下是其他一些方法:

    private static HttpWebRequest GetRequest(string url)
    {
        var request = (HttpWebRequest) WebRequest.Create(url);
        request.Timeout = 15000;
        request.Proxy = null;
        request.Headers.Add("Accept-Encoding", "gzip,deflate");

        return request;
    }

    private static Stream GetStreamForResponse(HttpWebResponse webResponse)
    {
        var responseStream = webResponse.GetResponseStream();
        if (responseStream == null)
            return null;

        Stream stream;
        switch (webResponse.ContentEncoding.ToUpperInvariant())
        {
            case "GZIP":
                stream = new GZipStream(responseStream, CompressionMode.Decompress);
                break;
            case "DEFLATE":
                stream = new DeflateStream(responseStream, CompressionMode.Decompress);
                break;
            default:
                stream = responseStream;
                break;
        }
        return stream;
    }

所以我的问题是:为什么这么大的差异?我认为当我使用等待I / O操作时 - 这是一种好方法,它是执行HTTP请求的最佳方式。但在实践中并不完全正确。

在没有async / await的解决方案中,我们在等待响应时有线程阻塞,如果它不是Google,并且将是一个长时间运行的服务器,这个请求将阻止我的所有应用。

可能有更好的方法来解决我的问题吗?

P.S .: 这个“神奇”:     ServicePointManager.Expect100Continue = false; ServicePointManager.UseNagleAlgorithm = false;
只是来自互联网的一些建议,我不知道它是如何工作的。

更新 感谢Servy,我最终理解了我在测量中的错误。现在Process方法如下:

    private static void Process(string url)
    {
        var taskCollection = new List<Task>();
        for (var i = 1; i <= 100; i++)
        {
            var sw = Stopwatch.StartNew();
            taskCollection.Add(Task.Factory.StartNew(() =>
                                                     {
                                                         ProcessInternal(url);
                                                         sw.Stop();
                                                         Console.WriteLine(sw.ElapsedMilliseconds);
                                                     }));
        }
        Task.WaitAll(taskCollection.ToArray());
    }

现在第二个解决方案显示其毫秒(请求300-1000毫秒)。但无论如何它比async / await更快。 async / await方法必须更慢吗?

1 个答案:

答案 0 :(得分:1)

使用秒表来衡量整个操作的时间,你会发现时间会更加接近。

基本上,您可以更快地在异步版本中启动秒表,这就是他们出现这么长时间的原因。

在异步解决方案中,您可以同步启动每个操作,然后异步等待它们全部完成。在这种情况下,当应用程序启动时,秒表几乎都会立即启动。

对于您的同步解决方案,您甚至不会启动秒表,直到每个任务都被安排在线程池线程中运行,被分配给线程,并且实际上是从该线程运行的。很多等待都将在这里发生,你还没有开始你的秒表