I have a requirement where I can hit an API 5 times in a second. If I have to make a total of 50 requests, I want to make the first 5 requests and wait for 1 second before I can hit the API with another batch of 5 requests. I tried using Thread pool as well as Parallel task library For\Foreach loops and Task classes but I am unable to get a sequential counter that would tell me that 5 Tasks have been created. Here is a sample of what I am trying to do:
List<string> str = new List<string>();
for (int i = 0; i <= 100; i++)
{
str.Add(i.ToString());
}
Parallel.ForEach(str, new ParallelOptions { MaxDegreeOfParallelism = 5 },
(value, pls, index) =>
{
Console.WriteLine(value);// simulating method call
if (index + 1 == 5)
{
// need the main thread to sleep so next batch is
Thread.Sleep(1000);
}
});
Since you're using .NET 4.0 (and assuming, hopefully, that you're at least using VS2012), you can use Microsoft.Bcl.Async to get async-await features.
Once you do that, you can easily query your API endpoint asynchronously (no need for extra threads for that), and use a AsyncSemaphore (see implementation below) to cap the number of requests you do concurrently.
For example:
public readonly AsyncSemaphore = new AsyncSemaphore(5);
public readonly HttpClient httpClient = new HttpClient();
public async Task<string> LimitedQueryAsync(string url)
{
await semaphoreSlim.WaitAsync();
try
{
var response = await httpClient.GetAsync(url);
return response.Content.ReadAsStringAsync();
}
finally
{
semaphoreSlim.Release();
}
}
Now you can query it like this:
public async Task DoQueryStuffAsync()
{
while (someCondition)
{
var results = await LimitedQueryAsync(url);
// do stuff with results
await Task.Delay(1000);
}
}
Edit:
As @ScottChamberlain points out correctly, SemaphoreSlim isn't avaliable in .NET 4. You can instead use AsyncSemaphore, which looks as follows:
public class AsyncSemaphore
{
private readonly static Task s_completed = Task.FromResult(true);
private readonly Queue<TaskCompletionSource<bool>> m_waiters =
new Queue<TaskCompletionSource<bool>>();
private int m_currentCount;
public AsyncSemaphore(int initialCount)
{
if (initialCount < 0)
{
throw new ArgumentOutOfRangeException("initialCount");
}
m_currentCount = initialCount;
}
public Task WaitAsync()
{
lock (m_waiters)
{
if (m_currentCount > 0)
{
--m_currentCount;
return s_completed;
}
else
{
var waiter = new TaskCompletionSource<bool>();
m_waiters.Enqueue(waiter);
return waiter.Task;
}
}
}
public void Release()
{
TaskCompletionSource<bool> toRelease = null;
lock (m_waiters)
{
if (m_waiters.Count > 0)
toRelease = m_waiters.Dequeue();
else
++m_currentCount;
}
if (toRelease != null)
toRelease.SetResult(true);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With