需要在Semaphore上调用Dispose吗?

10
class myclass 
{
    private Semaphore _sync;

    myclass ()
    {
          _sync = new Semaphore(1,1);
    }

    doasync()
    {
           _sync.WaitOne();
           //do some stuff
           _sync.Release();
    }
}


 somefunctionsomewhere() 
 {
     var myobject = new myclass();    
     //spawn 100 threads that all call myobject.doasync()
 }

上面的代码安全且干净吗?当100个线程完成异步工作时,这两个_sync变量是否会被处理并收集myclass?

如果不是,什么是最好的方法来确定何时调用_sync.Dispose()?

2个回答

12
简短回答: 不需要调用 Dispose()。
详细回答:
虽然建议在使用 Semaphore 后调用 Dispose(),但垃圾收集器最终会处理 Semaphore 的资源。
显式调用 Dispose() 将确保早日释放相关联的资源,但仅在短时间内创建并“遗忘”大量 Semaphore 实例时才需要。
如果调用了 Dispose(),则可以使用简单的 Dispose() 调用,不需要创建 finalizer ~myclass(),因为那只是重复了 Semaphore 实现已经完成的工作。

我更喜欢这个答案,因为显然如果垃圾回收最终会处理它,我不想做额外的工作。有点与其他答案矛盾,所以在接受之前会等待第三方的确认。 - Jamona Mican
3
不仅仅是因为垃圾回收器可能会处理它,而且Dispose方法会清理类使用的任何非托管资源,因此不能保证垃圾回收器一定会处理它。 - Bob Vale
但是Semaphore包含一个未管理的私有成员"m_waitHandle",如果您不在创建的SemaphoreSlim上调用Dispose(),那么m_waitHandle将不会关闭。因此,您可能会遇到一些内存泄漏问题。这不应该被接受为答案。源代码: https://github.com/microsoft/referencesource/blob/master/mscorlib/system/threading/SemaphoreSlim.cs#L871 - Paweł Kanarek
m_waitHandle是一个ManualResetEvent,如果你深入挖掘,你会发现一个finalizer将释放它,我在这里找到了一个:https://github.com/microsoft/referencesource/blob/master/mscorlib/system/runtime/interopservices/safehandle.cs#L197 - Armin
不会,因为 finalizer 调用 Dispose(false),只有在调用 true 的 Dispose 时才会释放 m_waitHandle。 - Paweł Kanarek

3
如果一个类实现了IDisposable接口,你应该正确地处理它。这不仅仅是依赖于垃圾收集器,可释放的类可能有需要正确清理的打开句柄/COM对象,而你不能保证垃圾收集器会处理它们。
理想情况下,你应该让myclass实现IDisposable接口,并在Dispose方法中清理你的可释放对象。如果你真的无法确定何时处理myclass实例,则可以在myclass的析构函数中实现逻辑来调用Dispose方法。
public class myclass : IDisposable
   {
   private Semaphore _sync;

    myclass ()
    {
          _sync = new Semaphore(1,1);
    }

    doasync()
    {
           _sync.WaitOne();
           //do some stuff
           _sync.Release();
    }

    public void Dispose()
       Dispose(true);
       GC.SuppressFinalize(this);
    {

    protected void Dispose(bool isDisposing)
    {
       var sync=Interlocked.Exchange(ref _sync,null);
       if (sync !=null) sync.Dispose();
    }


    // This is for when you cannot determine when to dispose of your object
    void ~myclass() {
       Dispose(false);
    }
}

你不需要在你的类中实现 IDisposable,因为 Semaphore 已经实现了它。 - Monsignor
@Monsingor 不,你需要让你的类实现IDisposable接口,因为信号量是私有的。你让你的类实现IDisposable接口,这样就可以调用信号量的dispose方法了。 - Bob Vale
@HarryMexican 我也意识到我之前关于GC处理的评论并不一定正确。IDisposable的重点在于dispose方法清理任何非托管资源,因此不一定会被GC正确处理。 - Bob Vale
@Bob Vale 抱歉,我的意思是你不需要实现终结器。 - Monsignor
1
@Bob Vale 没有必要实现 finalizer 来仅调用子对象的 Dispose。这没有意义,因为当 GC 调用 finalizer 时,您的对象及其所有子代都被视为垃圾。整个树将在同一 GC 运行期间被销毁。只有在您的类直接使用 非受控 资源的情况下才应该实现 finalizer。Semaphore 是托管资源,它会自行处理其释放。 - Monsignor
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接