0

using (var foo = bar){} is excellent syntactic sugar. It replaces an entire blob

var foo = bar
try
{
}
finally
{
    foo.dispose()
}

I found myself today writing very similar blobs

var foo.WaitOne();
try
{
}
finally
{
    foo.release()
}

I don't suppose there is similar sugar for this in C#?

Servy
  • 202,030
  • 26
  • 332
  • 449
Sidney
  • 624
  • 7
  • 20

3 Answers3

3

No. There is one for Monitor (lock) but none for semaphore.

If you really want you can create helper disposable class to use it with using but it may be considered abuse of IDisposable pattern.

Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
2

As adviced by Alexei, your best chance is to mock the requested behavior using an helper class which implements IDisposable.

Something like this should suffice:

public static class AutoReleaseSemaphoreExtensions
{
    // single threaded + idempotent Dispose version
    private sealed class AutoReleaseSemaphore : IDisposable
    {
        private readonly Semaphore _semaphore;

        private bool _disposed = false;

        public AutoReleaseSemaphore(Semaphore semaphore)
        {
            _semaphore = semaphore;
        }

        public void Dispose()
        {
            if(_disposed) return;
            _semaphore.Release();
            _disposed = true;
        }
    }

    public static IDisposable WaitOneAndRelease(this Semaphore semaphore)
    {
        semaphore.WaitOne();
        return new AutoReleaseSemaphore(semaphore);
    }
}

Which may be used in the following way (thanks to extension methods):

var sem = new Semaphore(0, 1); // your semaphore here

using (sem.WaitOneAndRelease())
{
    // do work here
}
// semaphore is released outside using block.
Federico Dipuma
  • 17,655
  • 4
  • 39
  • 56
  • This could be improved by ensuring that `Dispose` is idempotent. Right now if `Dispose` gets called multiple times, the semaphore will get released multiple times. Something like `public void Dispose() { var s = Interlocked.Exchange( ref _semaphore, null ); s?.Release(); }` and making sure `_semaphore` is not readonly. – Kyle Aug 17 '17 at 20:48
  • @Kyle you are certainly right. We may also avoid the use of any lock (or `Interlocked` call) by just implicitly restrict its usage inside a single thread (which, I believe, is the right thing to do). This way a simple `bool _disposed` field should be enough. – Federico Dipuma Aug 17 '17 at 20:55
  • I've seen this pattern elsewhere too. Leveraging the `using` keyword by boilerplating your (er, boilerplate) code into IDisposables is really, really, sweet. I mean that in a purely platonic and syntactic way. – code4life Aug 17 '17 at 21:04
0

Check out the lock statement.

lock calls Monitor.Enter and Monitor.Exit, but it's the only syntactic sugar blob-replacer related to thread synchronization.

The answer to this question expands it very well: How does lock work exactly?

Paolo Tedesco
  • 55,237
  • 33
  • 144
  • 193