Last active
November 10, 2016 18:56
-
-
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/
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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