C# 在 foreach 循环中发出异步 http 请求
C# Make async http requests within a foreach loop
我需要发出多个 Web 请求,其中 URI 位于 DataTable 中。早些时候我有下面的代码。但我意识到这会进行同步调用,因为 await 会等到 GET/POST 调用完成并处理响应,然后继续下一次迭代.
foreach (DataRow dr in dt.Rows)
{
activeTasks.Add(SendRequestAsync(dr));
Task.WhenAll(activeTasks).Wait();
}
private async Task<string> SendRequestAsync(DataRow dr)
{
using (var client = new HttpClient())
{
string reqMethod = (dr["RequestMethod"] != null && dr["RequestMethod"].ToString() != "") ? dr["RequestMethod"].ToString() : "GET";
client.BaseAddress = new Uri(dr["URL"].ToString());
client.DefaultRequestHeaders.Accept.Clear();
string reqContentType = (dr["RequestContentType"] != null && dr["RequestContentType"].ToString() != "") ? dr["RequestContentType"].ToString() : "text/xml";
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(reqContentType));
HttpResponseMessage response = null;
try
{
if (reqMethod == "GET")
response = await client.GetAsync(client.BaseAddress.AbsoluteUri);
else
response = await client.PostAsync(client.BaseAddress.AbsoluteUri, null);
response.EnsureSuccessStatusCode();
var responseText = await response.Content.ReadAsStringAsync();
return responseText;
}
catch (Exception e)
{
return "-1";
}
}
}
然后我遇到了 Parallel 功能并改用了 Parallel.ForEach。像这样:
Parallel.ForEach(rows, dr =>
{
activeTasks.Add(SendRequestAsync(dr));
Task.WhenAll(activeTasks).Wait();
});
这工作正常,实现了并行性,请求是异步的,与早期的解决方案相比,它在短时间内完成。
但问题是它不可靠 - 有时我会收到
这样的错误
- System.IndexOutOfRangeException: 索引超出数组范围
- System.InvalidOperationException: Collection被修改;枚举操作可能无法执行。
我们是否可以在 foreach 中实现 http 异步调用?
Parallel.ForEach
适用于 CPU 密集型操作,不适用于 I/O-intensive 操作或异步操作。
您可以在 foreach
循环中 await
就好了。不过,包含循环的方法本身需要异步。
正如@Johnathon_Chase 所说,只需将您的 WhenAll()
调用移到循环之外:
foreach (DataRow dr in dt.Rows)
{
activeTasks.Add(SendRequestAsync(dr));
}
Task.WhenAll(activeTasks).Wait();
for
循环填充集合,然后 Task.WhenAll()
在请求完成时阻塞。
我需要发出多个 Web 请求,其中 URI 位于 DataTable 中。早些时候我有下面的代码。但我意识到这会进行同步调用,因为 await 会等到 GET/POST 调用完成并处理响应,然后继续下一次迭代.
foreach (DataRow dr in dt.Rows)
{
activeTasks.Add(SendRequestAsync(dr));
Task.WhenAll(activeTasks).Wait();
}
private async Task<string> SendRequestAsync(DataRow dr)
{
using (var client = new HttpClient())
{
string reqMethod = (dr["RequestMethod"] != null && dr["RequestMethod"].ToString() != "") ? dr["RequestMethod"].ToString() : "GET";
client.BaseAddress = new Uri(dr["URL"].ToString());
client.DefaultRequestHeaders.Accept.Clear();
string reqContentType = (dr["RequestContentType"] != null && dr["RequestContentType"].ToString() != "") ? dr["RequestContentType"].ToString() : "text/xml";
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(reqContentType));
HttpResponseMessage response = null;
try
{
if (reqMethod == "GET")
response = await client.GetAsync(client.BaseAddress.AbsoluteUri);
else
response = await client.PostAsync(client.BaseAddress.AbsoluteUri, null);
response.EnsureSuccessStatusCode();
var responseText = await response.Content.ReadAsStringAsync();
return responseText;
}
catch (Exception e)
{
return "-1";
}
}
}
然后我遇到了 Parallel 功能并改用了 Parallel.ForEach。像这样:
Parallel.ForEach(rows, dr =>
{
activeTasks.Add(SendRequestAsync(dr));
Task.WhenAll(activeTasks).Wait();
});
这工作正常,实现了并行性,请求是异步的,与早期的解决方案相比,它在短时间内完成。 但问题是它不可靠 - 有时我会收到
这样的错误- System.IndexOutOfRangeException: 索引超出数组范围
- System.InvalidOperationException: Collection被修改;枚举操作可能无法执行。
我们是否可以在 foreach 中实现 http 异步调用?
Parallel.ForEach
适用于 CPU 密集型操作,不适用于 I/O-intensive 操作或异步操作。
您可以在 foreach
循环中 await
就好了。不过,包含循环的方法本身需要异步。
正如@Johnathon_Chase 所说,只需将您的 WhenAll()
调用移到循环之外:
foreach (DataRow dr in dt.Rows)
{
activeTasks.Add(SendRequestAsync(dr));
}
Task.WhenAll(activeTasks).Wait();
for
循环填充集合,然后 Task.WhenAll()
在请求完成时阻塞。