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);
}
}