I have the following class to manage access to a resource:
class Sync : IDisposable
{
private static readonly SemaphoreSlim Semaphore = new SemaphoreSlim(20);
private Sync()
{
}
public static async Task<Sync> Acquire()
{
await Semaphore.WaitAsync();
return new Sync();
}
public void Dispose()
{
Semaphore.Release();
}
}
Usage:
using (await Sync.Acquire())
{
// use a resource here
}
Now it allows not more than 20 shared usages.
How to modify this class to allow not more than N shared usages per unit of time (for example, not more than 20 per second)?
"20 per second" is completely different than "20 at a time". I recommend that you leave the thread synchronization behind and use higher-level abstractions capable of working more naturally with time as a concept.
In particular, Reactive Extensions has a number of different throttling operators.
Here's a basic reimplementation which calls Semaphore.Release
either when the specified time period has elapsed, or (optionally - see code comments in Dispose()
) when the Sync
instance is disposed.
class Sync : IDisposable
{
private static readonly SemaphoreSlim Semaphore = new SemaphoreSlim(20);
// 0 : semaphore needs to be released.
// 1 : semaphore already released.
private int State = 0;
private Sync()
{
}
// Renamed to conform to Microsoft's guidelines.
public static async Task<Sync> AcquireAsync(TimeSpan releaseAfter)
{
var sync = new Sync();
await Semaphore.WaitAsync().ConfigureAwait(false);
try
{
return sync;
}
finally
{
// Fire-and-forget, not awaited.
sync.DelayedRelease(releaseAfter);
}
}
private async void DelayedRelease(TimeSpan releaseAfter)
{
await Task.Delay(releaseAfter).ConfigureAwait(false);
this.ReleaseOnce();
}
private void ReleaseOnce()
{
// Ensure that we call Semaphore.Release() at most
// once during the lifetime of this instance -
// either via DelayedRelease, or via Dispose.
if (Interlocked.Exchange(ref this.State, 1) == 0)
{
Semaphore.Release();
}
}
public void Dispose()
{
// Uncomment if you want the ability to
// release the semaphore via Dispose
// thus bypassing the throttling.
//this.ReleaseOnce();
}
}
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