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

时间:2017-06-13 16:39:34

标签: c# asp.net .net async-await semaphore

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

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

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

我对c#不熟悉,WebRequests/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; } 非常新,所以感谢任何帮助。我试着在这里添加所有相关代码。如果您还有其他需要,请告诉我。

<style>

    .velo {
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background-color: #000000;
        opacity: 0.3;
        position: absolute;
        z-index: 99999999;
    }
    .preloader {
        background: url(myroute/preloader.gif) no-repeat center;
        position: fixed;
        left: 0;
        top: 0;
        width: 100%;
        z-index: 999999999999;
        height: 100%;
    }

1 个答案:

答案 0 :(得分:3)

我认为你完全不了解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);
    }
    
  3. 您真正需要做的是将信号量移至实例级(甚至是带有static关键字的类型级别),并等待内部 GetWeatherDataAsync,并将Release放入finally块。

    至于Parallel.Foreach - 你不应该在这种情况下使用它,因为它不知道async方法(它是在async/await之前引入的),以及你的方法看起来他们不受CPU限制。