根据文档:
是否有特殊资源被"
SemaphoreSlim
不使用Windows内核信号量"。
SemaphoreSlim
使用,使得在不再使用SemaphoreSlim
时调用Dispose
变得重要?是否有特殊资源被"
SemaphoreSlim
不使用Windows内核信号量"。
SemaphoreSlim
使用,使得在不再使用SemaphoreSlim
时调用Dispose
变得重要?AvailableWaitHandle
属性,那么是的,您必须调用 Dispose()
方法来清理未经处理的资源。AvailableWaitHandle
,那么不需要调用 Dispose()
方法来执行任何重要操作。
SemaphoreSlim
将根据需要创建一个 ManualResetEvent
,如果您访问 AvailableWaitHandle
属性,则可能会派上用场。例如,如果您需要等待多个句柄。如果您访问了 AvailableWaitHandle
属性,然后未调用 Dispose()
方法,您将拥有一个泄漏的 ManualResetEvent
,它包装了对未经处理的 CreateEvent
资源的句柄,需要相应的 CloseHandle
调用以进行清理。IDisposable
接口的任何对象时,应该调用 Dispose()
方法。在这种情况下,尽管技术上可能是安全的,但忽略此做法存在几个风险:SemaphoreSlim
的行为,使得需要调用 Dispose()
方法。SemaphoreSlim
在类外部暴露,调用代码可能会引用 AvailableWaitHandle
属性,而没有意识到您的类没有处置 SemaphoreSlim
,从而创建未经处理的资源泄漏。可以。
它可能使用一个ManualResetEvent
,该事件使用SafeHandle
的SafeWaitHandle
,并且它具有非托管句柄。
您可以在此参考源代码中看到它。
SafeHandle
是可终结的,因此如果您不释放它(通过释放SemaphoreSlim
),它将进入终结器,终结器需要为您执行释放操作。由于终结器是单线程的,因此在某些情况下可能会过度使用,因此始终建议处理可终结对象。
对于任何实现了 IDisposable
接口的类,您应该始终调用 Dispose()
方法(或将其放在 using
语句中),而不是基于其内部实现来做出决定。类的作者已经通过实现 IDisposable
接口为您做出了这个决定。
IDisposable
接口的决定并不总是由类的作者来做。有许多接口(如 IEnumerator<T>
)继承了 IDisposable
,以允许某些实现在使用后需要进行清理。即使实现 IEnumerable<T>
接口的类的契约指定其特定的 GetEnumerator
实现将始终返回一个可以安全地丢弃的对象,任何由 IEnumerable<T>.GetEnumerator
返回的对象都必须实现 IDisposable
接口,无论它是否需要进行清理。 - supercat对于许多其他类,我同意i3arnon的看法,但对于SemaphoreSlim,我会选择Tim的评论。如果您在低级类中使用SemaphoreSlim并且必须处理它,则实际上程序中的几乎所有内容都将成为IDisposable,而事实上这是不必要的。考虑到AvailableWaitHandle相当专业化且通常不会被使用,这一点更加真实。
为了防止其他编码人员访问AvailableWaitHandle,您可以将其包装在一个非一次性类中。例如,您可以在Cleary和Hanselman的包装器中看到这一点,两者都基于Stephen Toub的帖子(顺便说一句,该帖子没有Dispose)。
P.S. 关于IDisposable协定,应在文档中指定只有在访问AvailableWaitHandle时才需要Dispose。
IDisposable
接口,客户端有责任控制实例的生命周期,并在生命周期结束时调用IDisposable.Dispose
或IAsyncDisposable.DisposeAsync
。实现细节可能随着库的下一个版本而发生变化。今天不处理一个实例可能会被原谅,但明天它将破坏你的代码并产生意想不到的副作用。 - user21970328
HttpClient
告诉我们不要盲目地处理所有实现了IDisposable
接口的对象。 - user247702HttpClient
,而是如果你每次发送 HTTP 请求都创建一个新实例,这将阻止你充分利用连接重用。 - Matthew