Task.StartNew() vs . Parallel.ForEach:多个Web请求场景
本文关键字:Web 多个 请求 ForEach StartNew vs Parallel Task | 更新日期: 2023-09-27 17:53:49
我已经阅读了SO中的所有相关问题,但是对于我的场景中触发多个web服务调用的最佳方法有点困惑。
我有一个聚合器服务,它接受输入,解析并将其转换为多个web请求,使web请求调用(不相关,因此可以并行触发),并合并发送回调用者的响应。下面的代码正在使用-
list.ForEach((object obj) =>
{
tasks.Add(Task.Factory.StartNew((object state) =>
{
this.ProcessRequest(obj);
}, obj, CancellationToken.None,
TaskCreationOptions.AttachedToParent, TaskScheduler.Default));
});
await Task.WhenAll(tasks);
await Task.WhenAll(tasks)
来自Scott Hanselman的帖子,上面说
Stephen说,从可伸缩性的角度来看,一个更好的解决方案是利用异步I/O。当你向对岸呼喊对于网络来说,没有理由(除了方便)阻塞线程在等待响应返回"
现有的代码似乎消耗了太多的线程,处理器时间在生产负载上飙升到100%,这让我思考。
另一种选择是使用Parallel。ForEach使用分区but和"block";电话,这对我的场景来说很好。
考虑到这都是"Async "工作而非"CPU边界";工作,和web请求不是长时间运行(最多3秒返回),我倾向于相信现有的代码是足够好的。但是这会提供比Parallel.ForEach更好的吞吐量吗?平行的。ForEach可能会使用"minimal"。由于分区和线程的最佳使用(?)我确实测试了Parallel。
目标是减少CPU时间,提高吞吐量,从而获得更好的可伸缩性。有没有更好的方法来并行处理web请求?
感谢所有的输入,谢谢。
编辑:代码示例中显示的ProcessRequest方法确实使用HttpClient及其async方法来触发请求(PostAsync, GetAsync, PutAsync)。
你真正想要的是将它们称为并发的,而不是在parallel中。即"同时",而不是"使用多个线程"。发出web请求调用(不相关,因此可以并行触发)
现有代码似乎占用了太多线程
是啊,我也这么认为。:)
考虑到这都是"异步IO"工作,而不是"CPU绑定"工作
那么所有这些都应该异步完成,并且不使用任务并行或其他并行代码。
正如Antii指出的,你应该让你的异步代码异步化:
public async Task ProcessRequestAsync(...);
那么你要做的是使用异步并发 (Task.WhenAll
)来消费它,而不是使用并行并发 (StartNew
/Run
/Parallel
):
await Task.WhenAll(list.Select(x => ProcessRequestAsync(x)));
如果您是CPU限制(您是- "处理器时间飙升至100% "),您需要减少CPU使用。Async IO对此没有任何帮助。如果有的话,它会导致更多的CPU占用(这里不明显)。
分析应用程序,看看是什么占用了这么多CPU时间,并优化代码。
你启动并行(并行,任务,异步IO)的方式对并行操作本身的效率没有任何影响。如果你以异步方式调用它,网络不会变得更快。硬件还是一样的。也没有更少的CPU使用。通过实验确定最优并行度,并选择适合该并行度的并行技术。如果是几十个,那么线程是完全没问题的。
在Task.Factory.StartNew中包装同步调用不会给您异步的任何好处。您应该使用适当的异步函数以获得更好的可伸缩性。请注意Scott Hanselman是如何在post中创建异步函数的。
例如public async Task<bool> ValidateUrlAsync(string url)
{
using(var response = (HttpWebResponse)await WebRequest.Create(url).GetResponseAsync())
return response.StatusCode == HttpStatusCode.Ok;
}
结帐http://blogs.msdn.com/b/pfxteam/archive/2012/03/24/10287244.aspx
,你的ProcessRequest方法应该像
一样异步实现public async Task<bool> ProcessRequestAsync(...)
那么你可以直接
tasks.Add(this.ProcessRequestAsync(obj))
如果你用task . factory . startnew启动task,即使你的ProcessRequest方法在内部进行异步调用,它也不能作为异步工作。如果你想用任务。工厂你应该让你的lambda也是异步的,像
tasks.Add(Task.Factory.StartNew(async (object state) =>
{
await this.ProcessRequestAsync(obj);
}, obj, CancellationToken.None, TaskCreationOptions.AttachedToParent, TaskScheduler.Default));