Skip to content

Instantly share code, notes, and snippets.

@RichardD2
Last active November 10, 2016 18:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RichardD2/c54f5e48c5fe0124c0b6a177569bb086 to your computer and use it in GitHub Desktop.
Save RichardD2/c54f5e48c5fe0124c0b6a177569bb086 to your computer and use it in GitHub Desktop.
Async throttle, inspired by code from Stephen Toub's blog - http://blogs.msdn.com/b/pfxteam/
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
public sealed class Throttle
{
private readonly Queue<TaskCompletionSource<IDisposable>> _waiters = new Queue<TaskCompletionSource<IDisposable>>();
private readonly WaitCallback _releaseCoreCallback;
private readonly Task<IDisposable> _releaserTask;
private readonly Releaser _releaser;
private readonly int _maxCount;
private int _currentCount;
public Throttle(int initialCount)
{
if (initialCount <= 0) throw new ArgumentOutOfRangeException();
_maxCount = _currentCount = initialCount;
_releaser = new Releaser(this);
_releaserTask = Task.FromResult((IDisposable)_releaser);
_releaseCoreCallback = ReleaseCore;
}
public Task<IDisposable> WaitAsync()
{
lock (_waiters)
{
if (_currentCount > 0)
{
_currentCount--;
return _releaserTask;
}
var waiter = new TaskCompletionSource<IDisposable>();
_waiters.Enqueue(waiter);
return waiter.Task;
}
}
private void Release()
{
TaskCompletionSource<IDisposable> toRelease = null;
lock (_waiters)
{
if (_waiters.Count > 0)
{
toRelease = _waiters.Dequeue();
}
else if (_currentCount < _maxCount)
{
_currentCount++;
}
else
{
throw new SemaphoreFullException();
}
}
if (toRelease != null)
{
ThreadPool.QueueUserWorkItem(_releaseCoreCallback, toRelease);
}
}
private void ReleaseCore(object state)
{
((TaskCompletionSource<IDisposable>)state).SetResult(_releaser);
}
private sealed class Releaser : IDisposable
{
private readonly Throttle _throttle;
public Releaser(Throttle throttle)
{
_throttle = throttle;
}
public void Dispose()
{
_throttle.Release();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment