Do I need to Dispose a SemaphoreSlim?
Asked Answered
M

4

44

According to the documentation:

"a SemaphoreSlim doesn't use a Windows kernel semaphore".

Are there any special resources used by the SemaphoreSlim which make it important to call Dispose when the SemaphoreSlim will no longer be used?

Maddalena answered 16/8, 2015 at 8:59 Comment(4)
You could check by yourself looking at the source code here, but a strong hint for you is: It implements IDisposable interface.Archery
@Archery HttpClient has taught us to be wary of blindly disposing everything that implements IDisposable.Caution
@Caution I disagree with that statement, the problem isn't disposing of HttpClient, it's the fact that you would otherwise be creating a new instance for every HTTP message, which prevents you from taking advantage of reusing connections.Extemporaneous
The actual problem with disposing httpclient is socket starvation.Offside
H
17

Yes.

It may use a ManualResetEvent that uses a SafeWaitHandle which is a SafeHandle and it has an unmanaged handle.

You can see it in the reference source here.

SafeHandle is finalizable so if you don't dispose of it (by disposing of the SemaphoreSlim) it will go to the finalizer that will need to do that for you. Since the finalizer is a single thread it may get overworked in certain situations so it's always advisable to dispose finalizable objects.

Highsmith answered 16/8, 2015 at 9:8 Comment(3)
SafeHandle will only be created if AvailableWaitHandle is called. If you are using SemaphoreSlim only inside a wrapper that does not call AvailableWaitHandle (as I suggest below) then the finalize/dispose issue will not occur.Bea
@Bea yes, I would agree. Even Task is disposable.. and we never actually dispose of it.Highsmith
Interesting, and for much the same reason — AsyncWaitHandle. See #3734780Bea
H
44

If you access the AvailableWaitHandle property, then Yes, you must call Dispose() to cleanup unmanaged resources.

If you do not access AvailableWaitHandle, then No, calling Dispose() won't do anything important.

SemaphoreSlim will create a ManualResetEvent on demand if you access the AvailableWaitHandle. This may be useful, for example if you need to wait on multiple handles. If you do access the AvailableWaitHandle property, and then fail to call Dispose() you will have a leaked ManualResetEvent, which presumably wraps a handle to an unmanaged CreateEvent resource that needs a corresponding call to CloseHandle to clean up.

As other posters have pointed out, you should call Dispose() when you are done with any object that implements IDisposable. In this case, there are several risks to ignoring that practice, even though it may technically be safe to do so:

  1. My statements are based the reference source code to .NET 4.6.1. There is always the slim (pun intended) possibility that some future version of the framework will change SemaphoreSlim to where Dispose() is required.
  2. If your SemaphoreSlim is exposed outside of your class, calling code might reference the AvailableWaitHandle property not realizing that your class isn't disposing the SemaphoreSlim and create an unmanaged resource leak.
Helix answered 28/8, 2016 at 21:25 Comment(0)
H
17

Yes.

It may use a ManualResetEvent that uses a SafeWaitHandle which is a SafeHandle and it has an unmanaged handle.

You can see it in the reference source here.

SafeHandle is finalizable so if you don't dispose of it (by disposing of the SemaphoreSlim) it will go to the finalizer that will need to do that for you. Since the finalizer is a single thread it may get overworked in certain situations so it's always advisable to dispose finalizable objects.

Highsmith answered 16/8, 2015 at 9:8 Comment(3)
SafeHandle will only be created if AvailableWaitHandle is called. If you are using SemaphoreSlim only inside a wrapper that does not call AvailableWaitHandle (as I suggest below) then the finalize/dispose issue will not occur.Bea
@Bea yes, I would agree. Even Task is disposable.. and we never actually dispose of it.Highsmith
Interesting, and for much the same reason — AsyncWaitHandle. See #3734780Bea
B
8

For many other classes I would agree with i3arnon, but for SemaphoreSlim I'll go with Tim's comment. If you use SemaphoreSlim in a low-level class and have to dispose it then practically everything in your program will become IDisposable when in fact it is not necessary. This is all the more true given that AvailableWaitHandle is quite specialized and usually is not used.

To protect against other coders accessing the AvailableWaitHandle, you can just wrap it in a non-disposable class. You see this for example in the wrappers by Cleary and by Hanselman, both based on a post by Stephen Toub (which by the way does not Dispose). The wrapper also can prevent the IDisposable question from arising.

Bea answered 18/9, 2017 at 12:41 Comment(3)
Library client code should not depend on low level details like "why disposal is necessary" and "when you can avoid it without causing issues". If an object implements IDisposable the client is responsible to control the lifetime of the instance and call IDisposable.Dispose or IAsyncDisposable.DisposeAsync when the lifetime ends. Implementation details can change anytime with the next version of the library. Not disposing an instance can be forgiven today but tomorrow it will break your code and produce unexpected side effects.Advent
Can you please link the posts by Hanselman/Toub/Cleary? Tried googling but haven't found any. Thanks.Ouzo
I have added the links (and adjusted the comment on IDisposable).Bea
H
7

You should always call Dispose() on any class implementing IDisposable (or put it inside a using statement) and not base your decision on its internal implementation. The class author already made that decision for you by implementing the IDisposable interface.

Hoenack answered 16/8, 2015 at 9:14 Comment(2)
This is indeed the general rule. My question is specifically about SemaphoreSlim. Note that SemaphoreSlim use-cases typically don't lend them towards using 'using'.Maddalena
The decision to implement IDisposable may not always lie with the class author. There are a number of interfaces such as IEnumerator<T> which inherit IDisposable to allow for the possibility that some implementations will need to be cleaned up after use. Even if the contract for a class which implements IEnumerable<T> were to specify that its particular implementation of GetEnumerator will always return an object that can be safely abandoned, any object returned by IEnumerable<T>.GetEnumerator must implement IDisposable whether or not it needs cleanup.Brinna

© 2022 - 2024 — McMap. All rights reserved.