简体   繁体   English

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

[英]C# Make async http requests within a foreach loop

I need to make multiple webrequests where URIs are in a DataTable.我需要发出多个 Web 请求,其中 URI 位于 DataTable 中。 Earlier I had the below code.早些时候我有下面的代码。 But I realized this makes synchronous calls as await would wait till GET/POST call is complete and response is processed then it proceeds to next iteration.但我意识到这会进行同步调用,因为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";
        }
    }
}

Then I came across Parallel feature and used Parallel.ForEach instead.然后我遇到了Parallel功能并改用Parallel.ForEach Like this:像这样:

Parallel.ForEach(rows, dr =>
{
    activeTasks.Add(SendRequestAsync(dr));
    Task.WhenAll(activeTasks).Wait();
});

This works fine, parallelism is achieved, requests are asynchronous and it completes within fraction of a time as compared to earlier solution.这工作正常,实现了并行性,请求是异步的,与早期的解决方案相比,它在很短的时间内完成。 But the problem is it is not reliable - at times I get errors like但问题是它不可靠 - 有时我会遇到类似的错误

  • System.IndexOutOfRangeException: Index was outside the bounds of the array System.IndexOutOfRangeException:索引超出了数组的范围
  • System.InvalidOperationException: Collection was modified; System.InvalidOperationException:集合已修改; enumeration operation may not execute.枚举操作可能无法执行。

Is there anyway we can achieve http async calls within a foreach?无论如何,我们可以在 foreach 中实现 http 异步调用吗?

As @Johnathon_Chase said, just move your WhenAll() call outside of the loop:正如@Johnathon_Chase 所说,只需将您的WhenAll()调用移出循环:

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

The for loop populates the collection, and then Task.WhenAll() blocks while the requests complete. for循环填充集合,然后Task.WhenAll()在请求完成时阻塞。

Parallel.ForEach is for CPU-intensive operations and is not designed for I/O-intensive operations or for async. Parallel.ForEach用于 CPU 密集型操作,不是为 I/O 密集型操作或异步设计的。

You can await inside of a foreach loop just fine.您可以在foreach循环中await就好了。 The method containing your loop needs to be async itself, though.但是,包含循环的方法本身必须是异步的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM