Last week I had to deal with an external API that didn't allow for more that 50 requests per second.
On my journey on how to deal with this limitation I stumbled upon SemaphoreSlim Class.
SemaphoreSlim limits the number of threads that can access a resource concurrently. It seemed a good fit and guess what... It was! 😛
I won't bother you with more details, let's see a simplified example on how we can limit our concurrent requests to just 3 per second with code:
class Program
{
private static HttpClient httpClient = new HttpClient();
static async Task Main(string[] args)
{
var tasks = new List<Task>();
// here we set the max allowed concurrent requests, in our case it is 3
var throttler = new SemaphoreSlim(3);
// Let's say that we have 12 requests in total that we want to send
for (var i = 0; i < 12; i++)
{
// let's wait here until we can pass from the Semaphore
await throttler.WaitAsync();
// add our task logig here
tasks.Add(Task.Run(async () =>
{
try
{
var result = await ExecuteRequest();
// let's wait here for 1 second to honor the API's rate limit
await Task.Delay(1000);
}
finally
{
// here we release the throttler immediately
throttler.Release();
}
}));
}
// await for all the tasks to complete
await Task.WhenAll(tasks.ToArray());
}
private static async Task<HttpResponseMessage> ExecuteRequest()
{
var result = await httpClient.GetAsync("https://www.bing.com");
return result;
}
}
Please let me know in the comments below what is the equivalent of SemaphoreSlim class in other languages.
You can find the full code on my GitHub
Just run the project and use Fiddler or any other network tool to verify the throttled behaviour.
ALWAYS TRUST BUT VERIFY 😁
This post was written with love ❤️
Top comments (4)
Maybe someone can describe what is going on when adding this in the code to see which thread is being released.
// here we release the throttler immediately
throttler.Release();
Console.WriteLine(i.ToString());
The results are almost never the same and never seem to iterate in count as I would expect. Any ideas why this happens?
i
is almost always bigger than you expect? Rather than being passed by value, you're still referencing the shared variable; by the time the tasks start completing and logging, the loop has executed more than expected or completely.dotnetfiddle.net/5vNKSV
You are the Greatest American Hero Of All Time!
Short, Sweet, Helpful!
I have been working all day with Parallel.Foreach, and other async stuff, but this is working for me!!!
Thanks again! Pat
Thank you sooo much, I'm really happy I could assist! 😄