C#在foreach循环中发出异步http请求

C#在foreach循环中发出异步http请求,c#,async-await,parallel.foreach,C#,Async Await,Parallel.foreach,我需要在URI位于数据表中的位置发出多个WebRequest。之前我有下面的代码。但我意识到这会进行同步调用,因为await会等到GET/POST调用完成并处理响应后再进行下一次迭代 foreach (DataRow dr in dt.Rows) { activeTasks.Add(SendRequestAsync(dr)); Task.WhenAll(activeTasks).Wait(); } private async Task<string> SendReq

我需要在URI位于数据表中的位置发出多个WebRequest。之前我有下面的代码。但我意识到这会进行同步调用,因为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";
        }
    }
}
这工作得很好,实现了并行性,请求是异步的,与以前的解决方案相比,它只需几分钟就完成了。 但问题是它不可靠——有时我会遇到类似这样的错误

  • System.IndexOutOfRangeException:索引超出了数组的边界
  • System.InvalidOperationException:集合已修改;枚举操作不能执行

我们是否可以在foreach中实现http异步调用

并行。ForEach用于CPU密集型操作,而不是为I/O密集型操作或异步设计的


您可以在
foreach
循环中等待。但是,包含循环的方法本身必须是异步的。

并行的。ForEach
用于CPU密集型操作,而不是为I/O密集型操作或异步设计的


您可以在
foreach
循环中等待。但是,包含循环的方法本身需要是异步的。

正如@Johnathon_Chase所说,只需将
WhenAll()
调用移到循环之外:

foreach (DataRow dr in dt.Rows)
{
    activeTasks.Add(SendRequestAsync(dr));
}
Task.WhenAll(activeTasks).Wait();

for
循环填充集合,然后
Task.WhenAll()
在请求完成时阻塞。

正如@Johnathon\u Chase所说,只需将
WhenAll()
调用移到循环之外:

foreach (DataRow dr in dt.Rows)
{
    activeTasks.Add(SendRequestAsync(dr));
}
Task.WhenAll(activeTasks).Wait();

for
循环填充集合,然后
Task.WhenAll()
在请求完成时阻塞。

我不会为此使用
Parallel.ForEach
。实际上,您只想将
任务移动到将任务添加到任务列表中的循环之外。使用parallel foreach时,如果您决定同时进行调用,则要确保使用并发集合命名空间中的线程安全集合作为备用项,您可能需要增加每次调用的超时时间,因为在某些环境中,请求可能会被同时打开的最大套接字数阻止。来自:HttpClient旨在实例化一次,并在应用程序的整个生命周期中重复使用。为每个请求实例化一个HttpClient类将耗尽重载下可用的套接字数量。感谢所有人的回答,我比所有人都高:)我不会为此使用
Parallel.ForEach
。实际上,您只想将
任务移动到将任务添加到任务列表中的循环之外。使用parallel foreach时,如果您决定同时进行调用,则要确保使用并发集合命名空间中的线程安全集合作为备用项,您可能需要增加每次调用的超时时间,因为在某些环境中,请求可能会被同时打开的最大套接字数阻止。来自:HttpClient旨在实例化一次,并在应用程序的整个生命周期中重复使用。为每个请求实例化一个HttpClient类将耗尽重载下可用的套接字数量。感谢所有人的回复,我对所有人都投了赞成票:)