我正在试验/学习新的任务库,我用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)。我想我达到顶峰了吧?
就目前而言,您的代码是次优的,因为,尽管您使用的是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)也受到一些限制的约束。