繁体   English   中英

使用循环限制并发异步请求

[英]Throttling concurrent async requests with loop

我目前正在努力向Web API发出大量请求。 我试图async这个过程,以便我可以在合理的时间内完成,但是我无法限制连接,因此我不会发送超过10请求/秒。 我使用信号量进行限制但我不完全确定它在这种情况下是如何工作的,因为我有一个嵌套循环。

我基本上得到了一个模型列表,每个模型都有一个天数列表。 我需要在模型中每天提出请求。 天数可以是1到大约5099%的时间只有1 所以我想要async每个模型,因为它们将有大约3000个,但是我希望在需要完成多天的情况下async这些日子。 我需要保持在10请求/秒以下,所以我认为最好的方法是在整个操作中设置一个10的请求限制。 有没有一个地方可以放置信号量来限制与整个链的连接?

每个单独的请求还必须对2不同的数据进行两次请求,并且此API现在不支持任何类型的批处理。

我对c#来说是新手,非常新的asyncWebRequests/HttpClient新手,所以任何帮助都是值得赞赏的。 我试着在这里添加所有相关代码。 如果您还需要其他信息,请告诉我。

public static async Task GetWeatherDataAsync(List<Model> models)
{
    SemaphoreSlim semaphore = new SemaphoreSlim(10);
    var taskList = new List<Task<ComparisonModel>>();

    foreach (var x in models)
    {
        await semaphore.WaitAsync();
        taskList.Add(CompDaysAsync(x));
    }

    try
    {
        await Task.WhenAll(taskList.ToArray());
    }
    catch (Exception e) { }
    finally
    {
        semaphore.Release();
    }
}

public static async Task<Models> CompDaysAsync(Model model)
{
    var httpClient = new HttpClient();
    httpClient.DefaultRequestHeaders.Authorization = new 
                Headers.AuthenticationHeaderValue("Token","xxxxxxxx");
    httpClient.Timeout = TimeSpan.FromMinutes(5);
    var taskList = new List<Task<Models.DateTemp>>();

    foreach (var item in model.list)
    {
        taskList.Add(WeatherAPI.GetResponseForDayAsync(item, 
            httpClient, Latitude, Longitude));
    }
    httpClient.Dispose();
    try
    {
        await Task.WhenAll(taskList.ToArray());
    }
    catch (Exception e) { }

    return model;
}

public static async Task<DateTemp> GetResponseForDayAsync(DateTemp date, HttpClient httpClient, decimal? Latitude, decimal? Longitude)
{
    var response = await httpClient.GetStreamAsync(request1);
    StreamReader myStreamReader = new StreamReader(response);
    string responseData = myStreamReader.ReadToEnd();
    double[] data = new double[2];
    if (responseData != "[[null, null]]")
    {
        data = Array.ConvertAll(responseData.Replace("[", "").Replace("]", "").Split(','), double.Parse);
    }
    else { data = null; };

    double precipData = 0;
    var response2 = await httpClient.GetStreamAsync(request2);
    StreamReader myStreamReader2 = new StreamReader(response2);
    string responseData2 = myStreamReader2.ReadToEnd();
    if (responseData2 != null && responseData2 != "[null]" && responseData2 != "[0.0]")
    {
        precipData = double.Parse(responseData2.Replace("[", "").Replace("]", ""));
    }
    date.Precip = precipData;

    if (data != null)
    {
        date.minTemp = data[0];
        date.maxTemp = data[1];
    }
    return date;
}

我认为你完全不明白SemaphoreSlim做了什么。

  1. 您的信号量是基于方法级别的局部变量,因此每个 GetWeatherDataAsync方法调用都会产生对您的API的10调用,而无需等待其他客户端。
  2. 此外,如果models.Count > 10 ,您的代码将会死锁,因为您在每次迭代中等待信号量,这些请求正在堆叠,并且对于11th您的线程将永远挂起,因为您没有释放信号量:

     var semaphore = new SemaphoreSlim(10); foreach (var item in Enumerable.Range(0, 15)) { // will stop after 9 await semaphore.WaitAsync(); Console.WriteLine(item); } 

你真正需要做的是将信号量移动到实例级(甚至是带有static关键字的类型级),并 GetWeatherDataAsync等待它,并将Release放在finally块中。

对于Parallel.Foreach - 你不应该在这种情况下使用它,因为它不知道async方法(它是在async/await之前引入的),并且你的方法看起来不像是CPU绑定的。

暂无
暂无

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

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