C#在foreach循环中进行异步http请求

Vij*_*jay -1 c# async-await parallel.foreach

我需要在DataTable中的URI上进行多个Web请求。之前我有以下代码。但我意识到,这使得同步调用为的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";
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,我遇到了Parallel功能并改用了Parallel.ForEach。像这样:

Parallel.ForEach(rows, dr =>
{
    activeTasks.Add(SendRequestAsync(dr));
    Task.WhenAll(activeTasks).Wait();
});
Run Code Online (Sandbox Code Playgroud)

与早期的解决方案相比,该方法运行良好,可以实现并行性,请求是异步的,并且可以在短时间内完成。但是问题是它不可靠-有时会出现类似

  • System.IndexOutOfRangeException:索引超出数组的范围
  • System.InvalidOperationException:集合已修改;枚举操作可能无法执行。

无论如何,我们可以在foreach中实现http异步调用吗?

Kev*_*son 5

就像@Johnathon_Chase所说的那样,只需将您的WhenAll()调用移到循环外即可:

foreach (DataRow dr in dt.Rows)
{
    activeTasks.Add(SendRequestAsync(dr));
}
Task.WhenAll(activeTasks).Wait();
Run Code Online (Sandbox Code Playgroud)

for循环填充集合,然后Task.WhenAll()块,同时要求完成。