我已经转到了一些项目的.net核心,现在Parallel.ForEach有问题了。在过去,我经常有一个id值的列表,然后我会使用这些id值来进行web请求,以获得完整的数据。看起来会是这样的:
Parallel.ForEach(myList, l =>
{
// make web request using l.id
// process the data somehow
});在.net核心中,web请求都必须被标记为await,这意味着Parallel.ForEach操作必须使用async标记。但是,将Parallel.ForEach操作标记为async意味着我们有一个导致问题的void async方法。在我的特殊情况下,这意味着在并行循环中的所有web请求都完成之前,响应会返回到应用程序,这既尴尬又会导致错误。
问:这里使用Parallel.ForEach的替代方案是什么?
我找到的一个可能的解决方案是将并行循环封装在一个任务中,然后等待任务:
await Task.Run(() => Parallel.ForEach(myList, l =>
{
// stuff here
}));(在此发现:Parallel.ForEach vs Task.Run and Task.WhenAll)
但是,这对我不管用。当我使用它时,在循环完成之前,我仍然返回到应用程序。
另一种选择:
var tasks = new List<Task>();
foreach (var l in myList)
{
tasks.Add(Task.Run(async () =>
{
// stuff here
}));
}
await Task.WhenAll(tasks);这似乎可行,但这是唯一的选择吗?新的.net核心似乎使Parallel.ForEach几乎毫无用处(至少在嵌套的web调用方面是如此)。
如有任何协助/建议,将不胜感激。
发布于 2016-09-30 16:06:35
这三种分配方法都不是很好。
您不应该在此场景中使用Parallel类或Task.Run。
相反,有一个async处理程序方法:
private async Task HandleResponse(Task<HttpResponseMessage> gettingResponse)
{
HttpResponseMessage response = await gettingResponse;
// Process the data
}然后使用Task.WhenAll
Task[] requests = myList.Select(l => SendWebRequest(l.Id))
.Select(r => HandleResponse(r))
.ToArray();
await Task.WhenAll(requests);发布于 2016-09-30 17:28:15
注释中解释了为什么Parallel.ForEach不适合这个任务:它是为CPU绑定(CPU密集型)任务而设计的。如果您将其用于IO绑定操作(如发出web请求)--您将浪费线程池线程,等待响应时阻塞,这没有任何好处。仍然可以使用它,但对于这种情况不是最好的。
您需要的是使用异步web请求方法(比如HttpWebRequest.GetResponseAsync),但是这里有另一个问题--您不想一次执行所有的web请求(就像另一个答案所建议的那样)。您的列表中可能有数千个urls ( in )。因此,您可以使用为此设计的线程同步结构,例如Semaphore。Semaphore就像队列一样--它允许X线程通过,其余的线程应该等待某个繁忙的线程完成它的工作(稍微简化一些描述)。下面是一个示例:
static async Task ProcessUrls(string[] urls) {
var tasks = new List<Task>();
// semaphore, allow to run 10 tasks in parallel
using (var semaphore = new SemaphoreSlim(10)) {
foreach (var url in urls) {
// await here until there is a room for this task
await semaphore.WaitAsync();
tasks.Add(MakeRequest(semaphore, url));
}
// await for the rest of tasks to complete
await Task.WhenAll(tasks);
}
}
private static async Task MakeRequest(SemaphoreSlim semaphore, string url) {
try {
var request = (HttpWebRequest) WebRequest.Create(url);
using (var response = await request.GetResponseAsync().ConfigureAwait(false)) {
// do something with response
}
}
catch (Exception ex) {
// do something
}
finally {
// don't forget to release
semaphore.Release();
}
}发布于 2018-10-07 23:10:29
您应该使用ref关键字调用这些方法来完成任务,这应该会以最小的努力完成任务。在类似的情况下,这种方法对我很有效。
Parallel.ForEach(myList, l =>
{
// make web request using ref l.id
string id=l.id;
WebRequest webRequest= MakeRequest(ref id);
// process the data somehow
});
private WebRequest MakeRequest(ref string id)
{
//make and return web request
}https://stackoverflow.com/questions/39794498
复制相似问题