Почему SemaphoreSlim не выпускается в асинхронном задании? - PullRequest
0 голосов
/ 26 октября 2018

Мне нужно вызывать API тысячи раз как можно быстрее.API имеет ограничение в 10 вызовов в секунду.Чтобы в полной мере воспользоваться преимуществами 10 вызовов в секунду, не переходя, я вызываю API асинхронно и регулирую вызовы семафором и таймером.Мой код входит в семафор, вызывает API, а затем проверяет, что прошла хотя бы одна секунда, прежде чем он выпустит семафор.

Вызов API на самом деле довольно быстрый и возвращает примерно секунду или меньше, поэтому мойкод должен перейти прямо к логике проверки времени / выпуска семафора.Однако в действительности после 10 вызовов семафор заполняется и вообще не освобождается, пока не будут созданы остальные асинхронные задачи для вызова API.После этого все работает как положено, поэтому я не испытываю никаких реальных проблем.Поведение просто кажется странным, и я хотел бы понять это.

public static class MyClass 
{
    SemaphoreSlim semaphore = new SemaphoreSlim(10);

    public static async Task CreateCallApiTasks(IList<object> requests)
    {
        var callApiTasks = requests.Select(x => CallApi(x));
        await Task.WhenAll(callApiTasks);
    }

    private static async Task CallApi(object requestBody)
    {
        using (var request = new HttpRequestMessage(HttpMethod.Post, <apiUri>))
        {
            request.Content = new StringContent(requestBody, System.Text.Encoding.UTF8, "application/json");
            HttpResponseMessage response = null;

            using (var httpClient = new HttpClient())
            {
                var throttle = new Stopwatch();
                ExceptionDispatchInfo capturedException = null;

                await semaphore.WaitAsync();

                try
                {
                    throttle.Start();
                    response = await httpClient.SendAsync(request);

                    while (throttle.ElapsedMilliseconds < 1000)
                    {
                        await Task.Delay(1);
                    }

                    semaphore.Release();
                    throttle.Stop();
                }
                catch (Exception ex)
                {
                    capturedException = ExceptionDispatchInfo.Capture(ex);
                }

                if (capturedException != null)
                {
                    while (throttle.ElapsedMilliseconds < 1000)
                    {
                        await Task.Delay(1);
                    }

                    semaphore.Release();
                    throttle.Stop();                
                    capturedException.Throw();
                }
            }
        }
    }
}
...