C# .net核心并行.ForEach问题
我已经为一些项目切换到.net Core,现在Parallel.ForEach出现了问题。在过去,我经常有一个id值列表,然后我将使用这些id值进行web请求,以获取完整的数据。它看起来像这样:C# .net核心并行.ForEach问题,c#,parallel-processing,C#,Parallel Processing,我已经为一些项目切换到.net Core,现在Parallel.ForEach出现了问题。在过去,我经常有一个id值列表,然后我将使用这些id值进行web请求,以获取完整的数据。它看起来像这样: Parallel.ForEach(myList, l => { // make web request using l.id // process the data somehow }); 在.net Core中,web请求必须全部标记为wait,这意味着Parallel.Fo
Parallel.ForEach(myList, l =>
{
// make web request using l.id
// process the data somehow
});
在.net Core中,web请求必须全部标记为wait
,这意味着Parallel.ForEach操作必须标记为async
。但是,将Parallel.ForEach操作标记为async
意味着我们有一个导致问题的void async
方法。在我的特殊情况下,这意味着响应在并行循环中的所有web请求完成之前返回到应用程序,这既尴尬又会导致错误
问题:在这里使用Parallel.ForEach的替代方案是什么
我找到的一个可能的解决方案是将并行循环包装在任务中并等待任务:
await Task.Run(() => Parallel.ForEach(myList, l =>
{
// stuff here
}));
(可在此处找到:)
但是,这对我不起作用。当我使用它时,我仍然会在循环完成之前返回应用程序
另一种选择:
var tasks = new List<Task>();
foreach (var l in myList)
{
tasks.Add(Task.Run(async () =>
{
// stuff here
}));
}
await Task.WhenAll(tasks);
var tasks=newlist();
foreach(myList中的变量l)
{
tasks.Add(Task.Run)(异步()=>
{
//这里的东西
}));
}
等待任务。何时(任务);
这似乎可行,但这是唯一的选择吗?新的.net内核似乎使Parallel.ForEach变得毫无用处(至少在嵌套web调用方面是如此)
感谢您的任何帮助/建议。这三种方法都不好 您不应该使用
并行
类或任务。在此场景中运行
相反,使用async
处理程序方法:
private async Task HandleResponse(Task<HttpResponseMessage> gettingResponse)
{
HttpResponseMessage response = await gettingResponse;
// Process the data
}
这三种方法都不好 您不应该使用
并行
类或任务。在此场景中运行
相反,使用async
处理程序方法:
private async Task HandleResponse(Task<HttpResponseMessage> gettingResponse)
{
HttpResponseMessage response = await gettingResponse;
// Process the data
}
为什么
Parallel.ForEach
不适合此任务在注释中解释:它是为CPU限制(CPU密集型)任务设计的。如果您将它用于IO绑定操作(如发出web请求),那么在等待响应时阻塞的线程池线程将白白浪费。仍然可以使用它,但它并不适合这种情况
您需要的是使用异步web请求方法(如HttpWebRequest.GetResponseAsync),但还有另一个问题-您不希望同时执行所有web请求(如另一个答案所示)。您的列表中可能有数千个URL(ID)。因此,您可以使用为此而设计的线程同步结构,例如信号量
<代码>信号量类似于队列-它允许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();
}
}
静态异步任务进程URL(字符串[]URL){
var tasks=新列表();
//信号量,允许并行运行10个任务
使用(var信号量=新信号量lim(10)){
foreach(url中的变量url){
//在这里等待,直到有空间完成此任务
wait semaphore.WaitAsync();
添加(MakeRequest(信号量,url));
}
//等待其他任务完成
等待任务。何时(任务);
}
}
私有静态异步任务MakeRequest(信号量LIM信号量,字符串url){
试一试{
var request=(HttpWebRequest)WebRequest.Create(url);
使用(var response=await request.GetResponseAsync().ConfigureAwait(false)){
//做些有反应的事情
}
}
捕获(例外情况除外){
//做点什么
}
最后{
//别忘了释放
semaphore.Release();
}
}
为什么并行。ForEach
不适合此任务在注释中解释:它是为CPU限制(CPU密集型)任务设计的。如果您将它用于IO绑定操作(如发出web请求),那么在等待响应时阻塞的线程池线程将白白浪费。仍然可以使用它,但它并不适合这种情况
您需要的是使用异步web请求方法(如HttpWebRequest.GetResponseAsync),但还有另一个问题-您不希望同时执行所有web请求(如另一个答案所示)。您的列表中可能有数千个URL(ID)。因此,您可以使用为此而设计的线程同步结构,例如信号量
<代码>信号量类似于队列-它允许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();
}
}
静态异步任务进程URL(字符串[]URL){
var tasks=新列表();
//信号量,允许并行运行10个任务
使用(var信号量=新信号量lim(10)){
foreach(url中的变量url){
//在这里等待,直到有空间完成此任务
wait semaphore.WaitAsync();
添加(MakeRequest(信号量,url));
}
//等待其他任务完成
等待任务。何时(任务);
}
}
私有静态异步任务MakeRequest(信号量LIM信号量,字符串url){
试一试{
var request=(HttpWebRequest)WebRequest.Create(url);
使用(var response=await request.GetResponseAsync().ConfigureAwait(false)){
//做些有反应的事情
}
}
捕获(例外情况除外){
//做点什么
}
最后{
//别忘了释放
semaphore.Release();
}
}
您应该使用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
}
您应该使用ref关键字调用这些方法来完成任务,这应该以最小的工作量完成任务。这种方法有效