gpt4 book ai didi

c# - 异步任务 'Clogging'

转载 作者:太空狗 更新时间:2023-10-29 20:09:52 25 4
gpt4 key购买 nike

最近我开始尝试大量抓取一个网站以用于存档目的,我认为让多个 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 得出这个结果时:

Test results

如您所见,前四个任务以约 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();
}

更多解释:

线程池中可供使用的线程数量有限。这个数字会根据某些条件而变化,但总的来说它是正确的。出于这个原因,你永远不应该在线程池上做任何阻塞(如果你想实现并行性)。 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();
}

注意:这段代码还没有经过测试

阅读更多

有关 Task.Factory.StartNew 的更多信息以及应避免的原因:http://blog.stephencleary.com/2013/08/startnew-is-dangerous.html .

关于c# - 异步任务 'Clogging',我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28314695/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com