如何使用异步 Web 请求执行多线程
本文关键字:执行 多线程 请求 Web 何使用 异步 | 更新日期: 2023-09-27 18:31:16
我正在尝试实现 .NET 4 帮助程序/实用程序类,它应该根据 Web 测试工具的 url 列表检索 HTML 页面源代码。该解决方案应具有可伸缩性并具有高性能。
我已经在研究和尝试不同的解决方案很多天了,但找不到合适的解决方案。
根据我的理解,实现目标的最佳方法是使用使用 TPL 并行运行的异步 Web 请求。
为了完全控制标题等。我使用的是HttpWebResponse而不是包装HttpWebResponse的WebClient。在某些情况下,输出应链接到其他任务,因此使用 TPL 任务可能是有意义的。
经过许多不同的试验/方法,到目前为止我所取得的成就,
-
实现了基本的同步、异步 (APM) 和并行(使用 TPL 任务)解决方案,以查看不同解决方案的性能级别。
-
为了查看异步并行解决方案的性能,我使用了APM方法,BeginGetResponse和BeginRead,并在Parallel.ForEach中运行它。一切正常,我对表现很满意。不知何故,我觉得使用简单的 Parallel.ForEach 不是要走的路,例如我不知道如何使用任务链。
-
然后,我尝试了更复杂的系统,使用任务来包装 APM 解决方案,方法是使用 TaskCompletionSource 和迭代器遍历 APM 流。我相信这个解决方案可能是我正在寻找的,但是有一个奇怪的延迟,大约在 6-10 秒之间,在运行 2 个 urls 列表时会发生 500-3 次。
根据日志,执行已返回到线程,该线程在发生延迟时在循环中调用异步提取。当执行移回循环时,延迟并不总是发生,只有 2-3 次,其他时候它工作正常。看起来循环线程将创建一组任务,这些任务将由其他线程处理,虽然大多数/所有任务都已完成,但在循环继续创建剩余任务并且其他线程再次处于活动状态之前会有延迟(6-8s)。
循环内迭代器的原理是:
IEnumerable<Task> DoExample(string input)
{
var aResult = DoAAsync(input);
yield return aResult;
var bResult = DoBAsync(aResult.Result);
yield return bResult;
var cResult = DoCAsync(bResult.Result);
yield return cResult;
…
}
Task t = Iterate(DoExample(“42”));
我正在使用 System.Net.ServicePointManager.DefaultConnectionLimit 和超时使用 ThreadPool.RegisterWaitForSingleObject 来解决连接限制
我的问题很简单,实现用于检索 html 页面的帮助程序/实用程序类的最佳方法是什么:
- 可扩展并具有高性能
- 使用网络请求
- 轻松链接到其他任务
- 能够使用超时
- 使用 .NET 4 框架
如果您认为我上面介绍的使用APM,TaskCompletionSource和迭代器的解决方案很好,我将不胜感激任何帮助,以尝试解决延迟问题。
我对 C# 和 Windows 开发完全陌生,所以请不要介意我正在尝试的东西没有太大意义。
任何帮助将不胜感激,因为如果没有解决这个问题,我必须放弃我的测试工具开发。
谢谢
在TPL .NET之前,使用迭代器是一个很好的解决方案(例如,MS Robotics的协调和并发运行时(CCR)大量使用它们并帮助激发了TPL)。 一个问题是,仅靠迭代器并不能满足您的需求 - 您还需要一个调度程序来有效地分配工作负载。 这几乎是由您链接到的斯蒂芬·图布的片段完成的 - 但请注意一行:
enumerator.Current.ContinueWith(recursiveBody, TaskContinuationOptions.ExecuteSynchronously);
我认为您看到的间歇性问题可能与强制"同步执行"有关 - 它可能导致可用内核/线程之间的工作分布不均匀。
看看斯蒂芬在他的博客文章中提出的其他一些替代方案。 特别是,看看简单地链接 ContinueWith() 调用会做什么(如有必要,然后是匹配的 Unwrap() 调用)。 语法不会是最漂亮的,但它是最简单的,并且尽可能少地干扰底层工作窃取运行时,因此您有望获得更好的结果。