Friday, March 18, 2016

How to Release a Semaphore with a Using Block

I love that .NET has so many useful utilities available in the base framework. I often use the SemaphoreSlim, and I love that it supports async await. However, I don't like always having to create a try finally block around every use call to ensure that the release method gets called.

Below is a simple little extension method that will allow you to place the result of the Semaphore wait into a using block, thus ensuring that the dispose will always release the lock.

SemaphoreSlim Extensions

public static class SemaphoreSlimExtensions
{
    public static async Task<IDisposable> UseWaitAsync(
        this SemaphoreSlim semaphore, 
        CancellationToken cancelToken = default(CancellationToken))
    {
        await semaphore.WaitAsync(cancelToken).ConfigureAwait(false);
        return new ReleaseWrapper(semaphore);
    }
 
    private class ReleaseWrapper : IDisposable
    {
        private readonly SemaphoreSlim _semaphore;
 
        private bool _isDisposed;
 
        public ReleaseWrapper(SemaphoreSlim semaphore)
        {
            _semaphore = semaphore;
        }
 
        public void Dispose()
        {
            if (_isDisposed)
                return;
 
            _semaphore.Release();
            _isDisposed = true;
        }
    }
}

Unit Tests

public class SemaphoreSlimExtensionsTests
{
    [Fact]
    public async Task UseWaitAsync()
    {
        var semaphore = new SemaphoreSlim(1);
 
        Assert.Equal(1, semaphore.CurrentCount);
 
        using (await semaphore.UseWaitAsync())
        {
            Assert.Equal(0, semaphore.CurrentCount);
        }
 
        Assert.Equal(1, semaphore.CurrentCount);
    }
}

Enjoy,
Tom

2 comments:

  1. Sorry if you've answered this question elsewhere, but I couldn't find anything when I looked - is this code under any particular license?

    ReplyDelete

Real Time Web Analytics