【问题标题】:Should I use IDisposable for purely managed resources?我应该将 IDisposable 用于纯托管资源吗?
【发布时间】:2010-04-05 20:08:10
【问题描述】:

这是场景:

我有一个名为 Transaction 的对象,它需要确保在任何给定时间只有一个实体有权编辑它。

为了便于长期锁定,我让类生成一个可用于进行编辑的令牌对象。

你会这样使用它:

var transaction = new Transaction();

using (var tlock = transaction.Lock())
{
    transaction.Update(data, tlock);
}

现在,我希望TransactionLock 类实现IDisposable,以便清楚其用法。但是,我没有任何非托管资源可供处置。然而,TransctionLock 对象本身是一种“非托管资源”,因为 CLR 不知道如何正确完成它。

所有这一切都很好,花花公子,我只需使用 IDisposable 就可以了。

但是,当我尝试在终结器中执行此操作时,我的问题出现了:

~TransactionLock()
{
    this.Dispose(false);
}

如果可能,我希望终结器从锁定中释放事务。 如何在终结器中检测父事务 (this.transaction) 是否已完成?

我应该使用更好的模式吗?

另外,Transaction 类本身不必是一次性的,因为它不维护对锁的引用,也不关心它在进入坟墓时是否被解锁。


Transaction 类看起来像这样:

public sealed class Transaction
{
    private readonly object lockMutex = new object();

    private TransactionLock currentLock;

    public TransactionLock Lock()
    {
        lock (this.lockMutex)
        {
            if (this.currentLock != null)
                throw new InvalidOperationException(/* ... */);

            this.currentLock = new TransactionLock(this);
            return this.currentLock;
        }
    }

    public void Update(object data, TransactionLock tlock)
    {
        lock (this.lockMutex)
        {
            this.ValidateLock(tlock);

            // ...
        }
    }

    internal void ValidateLock(TransactionLock tlock)
    {
        if (this.currentLock == null)
            throw new InvalidOperationException(/* ... */);

        if (this.currentLock != tlock)
            throw new InvalidOperationException(/* ... */);
    }

    internal void Unlock(TransactionLock tlock)
    {
        lock (this.lockMutex)
        {
            this.ValidateLock(tlock);

            this.currentLock = null;
        }
    }
}

以及TransactionLock 的Dispose(bool) 代码:

private void Dispose(bool disposing)
{
    if (disposing)
    {
        if (this.Transaction != null)
        {
            this.Transaction.Unlock(this);
            this.Transaction = null;
        }
    }
}

【问题讨论】:

  • 在什么情况下TransactionLock 可以在没有Dispose 被调用的情况下被实际放弃?我看不出终结器能够做任何有用的事情。

标签: c# .net dispose idisposable


【解决方案1】:

这是discussed before。不过,您的情况要容易得多,您也在实现终结器。这是根本错误的,您在客户端代码中隐藏了一个错误。请注意终结器在单独的线程上运行。调试一致的死锁比处理随机和异步消失的锁要容易得多。

建议:遵循 .NET 框架的引导:不要提供太多帮助。微软出于同样的原因放弃了 Synchronized 方法。

【讨论】:

  • 这与之前提到的完全不同。在我的例子中,被处理的令牌对象实际上是作为一种验证机制使用的,需要适当地清理。在我的场景中需要进行的阶段与原生句柄完全相同。
  • 好吧,有没有办法在不实际实现IDisposable 的情况下获得类似using 的语义?我很确定答案是否定的。在这种情况下,有没有办法确定性地触发终结器?
  • 另外,我刚刚发布了我的 Dispose 代码,如您所见,它并没有释放锁(因为我无法证明交易是否已经完成)。这意味着这将导致您提到的一致死锁(或者实际上是InvalidOperationException)。这实际上意味着我的终结器总是浪费,因为它从未进行任何实际处理。我希望喜欢它来处理锁,但我想,正如你所建议的那样,那是“帮助太多”......
【解决方案2】:

如何在终结器中检测是否 父交易 (this.transaction) 已经 确定了吗?

这可以通过在 Transaction 中保留一个 _disposed 布尔字段并通过 IsDisposed 只读属性公开它来实现。这是标准做法。

   ~TransactionLock()
    {
        this.Dispose(false);
    }

我应该有更好的模式吗 使用?

如果 TransactionLock 没有非托管资源是正确的,那么只需省略析构函数(终结器)。它没有任何功能,但它确实有相当大的成本。

编辑:如果我没看错,Unlock 不会切断从 TransactionLock 到 TTransaction 的链接,这意味着旧锁 Dispose(bool) 将通过析构函数被调用。目前尚不清楚这是否安全。

使用TransactionLock.Dispose(bool)的代码问题会更完整


此外,Transaction 类本身 不必是一次性的,因为它 不维护对 锁,不管有没有 当它进入时它被解锁 坟墓。

由此可知,当一个 TransactionLock 被收集时,它只能持有一个对同样被收集的 Transaction 的引用。此处无需干预析构函数,这不会解决任何问题,只会产生您不需要的问题。

【讨论】:

  • 我想,那么我真正想做的是在 C# 中模拟 RAII,通过强制(或建议)一个 Dispose() 而不是析构函数/终结器。有没有办法实现当对象超出范围时立即调用的终结器?
  • @John:对于 RAII,只需实现 Dispose() 并使用 using。仍然不需要析构函数。
猜你喜欢
  • 1970-01-01
  • 2011-12-20
  • 2015-10-28
  • 2012-05-05
  • 2013-09-29
  • 2012-01-26
  • 1970-01-01
  • 2012-05-06
  • 2013-05-12
相关资源
最近更新 更多