c# WebClient with Task.运行时只达到5%的网络使用率.为什么

本文关键字:网络 使用率 为什么 with WebClient Task 运行时 | 更新日期: 2023-09-27 18:19:03

我正在试验/学习新的任务库,我用WebClient和Task. run写了一个非常简单的html下载程序。然而,我的网络使用率从来没有超过5%。我想了解为什么以及如何改进我的代码以达到100%的网络使用/吞吐量(可能不可能,但它必须远远超过5%)。

我也希望能够限制线程的数量,但它似乎不像我想象的那么容易(即自定义任务调度程序)。是否有一种方法可以这样设置最大线程数:something. setmaxthread (2)?

internal static class Program
    {
        private static void Main()
        {
            for (var i = 0; i < 1000000; i++)
            {
                Go(i, Thread.CurrentThread.ManagedThreadId);
            }
            Console.Read();
        }
        private static readonly Action<int, int> Go = (counter, threadId) => Task.Run(() =>
        {
            var stopwatch = new Stopwatch();
            stopwatch.Start();
            var webClient = new WebClient();
            webClient.DownloadString(new Uri("http://stackoverflow.com"));
            stopwatch.Stop();
            Console.Write("{0} == {1} | ", threadId.ToString("D3"), Thread.CurrentThread.ManagedThreadId.ToString("D3"));
            Console.WriteLine("{0}: {1}ms ", counter.ToString("D3"), stopwatch.ElapsedMilliseconds.ToString("D4"));
        });
    }

根据@spender,这是async版本。然而,我的理解是,await将"记住"时间点,并将下载交给操作系统级别并跳过(2 console.write),并立即返回main,并继续在for循环中调度剩余的Go方法。我理解得对吗?UI上没有阻塞

private static async void Go(int counter, int threadId)
{
    using (var webClient = new WebClient())
    {
        var stopWatch = new Stopwatch();
        stopWatch.Start();
        await webClient.DownloadStringTaskAsync(new Uri("http://ftp.iinet.net.au/test500MB.dat"));
        stopWatch.Stop();
        Console.Write("{0} == {1} | ", threadId.ToString("D3"), Thread.CurrentThread.ManagedThreadId.ToString("D3"));
        Console.WriteLine("{0}: {1}ms ", counter.ToString("D3"), stopWatch.ElapsedMilliseconds.ToString("D4"));
    }
}

我注意到的是,当我下载大文件时,下载速度/网络使用方面没有太大的区别。它们(线程版本和异步版本)的峰值都是12.5%的网络使用率和12MByte的下载/秒。我还尝试运行多个实例(多个.exe运行),再次没有两者之间的巨大差异。当我试图同时从2个url(20个实例)下载大文件时,我得到了类似的网络使用(12.5%)和下载速度(10-12MByte/sec)。我想我达到顶峰了吧?

c# WebClient with Task.运行时只达到5%的网络使用率.为什么

就目前而言,您的代码是次优的,因为,尽管您使用的是Task。运行来创建在ThreadPool中运行的异步代码,在ThreadPool中运行的代码仍然阻塞在以下行:

webClient.DownloadString(...

这相当于滥用ThreadPool,因为它不是为运行阻塞任务而设计的,并且在处理工作负载高峰时启动额外线程的速度很慢。这反过来又会对任何使用ThreadPool的API(计时器,异步回调,它们无处不在)的顺利运行产生严重的影响,因为它们会将工作安排到线程池(饱和)队列的后面(不愿意地捆绑起来,慢慢地旋转数百个线程,这些线程将花费99.9%的时间无所事事)。

停止阻塞ThreadPool并切换到正确的不阻塞的异步方法

所以现在你可以用以下简单的mod来破坏你的路由器并严重惹恼So站点管理员:

   private static void Main()
    {
        for (var i = 0; i < 1000000; i++)
        {
            Go(i, Thread.CurrentThread.ManagedThreadId);
        }
        Console.Read();
    }
    private static async Task Go(int counter, int threadId)
    {
        var stopwatch = new Stopwatch();
        stopwatch.Start();
        using (var webClient = new WebClient())
        {
            await webClient.DownloadStringTaskAsync(
                             new Uri("http://stackoverflow.com"));
        }
            //...
    }

HttpWebRequest(因此WebClient)也受到一些限制的约束。