Alp*_*lta 7 c# parallel-processing asynchronous task
最近我开始尝试大规模搜索网站以进行存档,我认为让多个Web请求异步工作以加快速度是个好主意(10,000,000页肯定会存档很多)所以我冒险进入三分钟之后我就开始怀疑为什么我正在创造的任务(通过Task.Factory.StartNew)是"堵塞".
懊恼和好奇我决定测试这个以确定它是不是仅仅是环境的结果,所以我在VS2012中创建了一个新的控制台项目并创建了这个:
static void Main(string[] args)
{
for (int i = 0; i < 10; i++) {
int i2 = i + 1;
Stopwatch t = new Stopwatch();
t.Start();
Task.Factory.StartNew(() => {
t.Stop();
Console.ForegroundColor = ConsoleColor.Green; //Note that the other tasks might manage to write their lines between these colour changes messing up the colours.
Console.WriteLine("Task " + i2 + " started after " + t.Elapsed.Seconds + "." + t.Elapsed.Milliseconds + "s");
Thread.Sleep(5000);
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("Task " + i2 + " finished");
});
}
Console.ReadKey();
}
Run Code Online (Sandbox Code Playgroud)
运行时出现了这个结果:

正如您所看到的,前四个任务在快速连续时间内以~0.27的时间内开始,然而在此之后,任务开始时的任务开始急剧增加.
为什么会发生这种情况,我该怎么做才能解决或解决这个限制?
任务(默认情况下)在线程池上运行,就像它听起来一样,是一个线程池.线程池针对很多情况进行了优化,但投入Thread.Sleep其中可能会在大多数情况下抛出一个扳手.此外,Task.Factory.StartNew使用通常是一个坏主意,因为人们不理解它是如何工作的.试试这个:
static void Main(string[] args)
{
for (int i = 0; i < 10; i++) {
int i2 = i + 1;
Stopwatch t = new Stopwatch();
t.Start();
Task.Run(async () => {
t.Stop();
Console.ForegroundColor = ConsoleColor.Green; //Note that the other tasks might manage to write their lines between these colour changes messing up the colours.
Console.WriteLine("Task " + i2 + " started after " + t.Elapsed.Seconds + "." + t.Elapsed.Milliseconds + "s");
await Task.Delay(5000);
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("Task " + i2 + " finished");
});
}
Console.ReadKey();
}
Run Code Online (Sandbox Code Playgroud)
线程池可以使用有限数量的线程.该数字根据某些条件而变化,但是,一般情况下也是如此.出于这个原因,你永远不应该在线程池上做任何阻塞(如果你想实现并行性).Thread.Sleep是一个阻塞API的完美示例,但大多数Web请求API也是如此,除非您使用较新的异步版本.
因此,原始程序中的爬网问题可能与您发布的示例中的问题相同.您正在阻止所有线程池线程,因此它被强制启动新线程,并最终阻塞.
巧合的是,Task.Run以这种方式使用也很容易让你以一种你知道它何时完成的方式重写代码.通过存储对所有已启动任务的引用,并在最后等待它们(这不会阻止并行性),您可以可靠地知道所有任务何时完成.以下显示了如何实现:
static void Main(string[] args)
{
var tasks = new List<Task>();
for (int i = 0; i < 10; i++) {
int i2 = i + 1;
Stopwatch t = new Stopwatch();
t.Start();
tasks.Add(Task.Run(async () => {
t.Stop();
Console.ForegroundColor = ConsoleColor.Green; //Note that the other tasks might manage to write their lines between these colour changes messing up the colours.
Console.WriteLine("Task " + i2 + " started after " + t.Elapsed.Seconds + "." + t.Elapsed.Milliseconds + "s");
await Task.Delay(5000);
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("Task " + i2 + " finished");
}));
}
Task.WaitAll(tasks.ToArray());
Console.WriteLine("All tasks completed");
Console.ReadKey();
}
Run Code Online (Sandbox Code Playgroud)
注意:此代码尚未经过测试
有关Task.Factory.StartNew应该避免的更多信息和原因:http://blog.stephencleary.com/2013/08/startnew-is-dangerous.html.