我需要尽快调用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();
}
}
}
}
}