【问题标题】:Just how 'disposable' is ReaderWriterLockSlim?ReaderWriterLockSlim 到底有多“一次性”?
【发布时间】:2014-12-30 16:42:46
【问题描述】:

我主要遵循IDisposable 模式,对于大多数课程来说这是合理的。但是ReaderWriterLockSlim 让我质疑应用这种模式的可行性。 ReaderWriterLockSlim.Dispose 所做的只是关闭一些事件句柄。那么资源这么少的Dispose这样的课程有多重要呢?在这种情况下,我真的不介意 GC 是否必须再次等待非托管资源的终结器完成。

应用IDisposable 模式的后果是相当可观的,然而,每个使用一次性类的类现在也必须实现IDisposable。在我的特殊情况下,我正在为HashSet 实现一个包装器。我并不特别期望处理此类对象的要求,因为意外地,它使用了一个同步器。

在这种情况下有什么理由不违反一次性模式吗?虽然我渴望这样做,但在实践中我不会这样做,因为违反一致性会更糟糕。

【问题讨论】:

  • 您的问题是您使用的非静态锁与您的对象具有相同的生命周期?
  • @NathanCooper - 大致上,是的。为每个读/写操作实例化一个锁也不是很有效。我可以使用Monitor,但我担心锁定车队。
  • 所以你正在读或写东西,它不是静态的?所以它是每个实例的一些资源。
  • 是的,目前该类由HashSet(T)ReaderWriterLockSlim 组成,后者同步任何操作。
  • 很少需要处理这样的对象,它往往会在程序的生命周期内存活。不要浪费任何时间在程序终止前一毫秒处理它。

标签: c# .net garbage-collection dispose idisposable


【解决方案1】:

非托管操作系统句柄的问题是handles come from a limited supply. GC 没有意识到这一点。

句柄的纯内存消耗并没有那么大。只不过是内核内存中的一个对象,并且可能是某个地方的哈希表条目。

您是对的,仅仅说:“您必须始终处理所有一次性物品”是不够的。这条规则太简单了。 For example the Task class does not need to be disposed.如果你知道你在做什么,你可以对处置采取更宽松的立场。请注意,并非所有团队成员都可能理解这一点(现在您可以在源代码中留下指向此答案的链接...)。

如果您确定不会泄漏很多句柄,则可以安全地执行此操作。请注意,在边缘条件下(负载、错误等),您可能会泄漏比预期更多的内容,从而导致生产问题。

【讨论】:

    【解决方案2】:

    如果此字段为staticyou don't need to dispose of it,它将(正确)与您的应用程序具有相同的生命周期。我知道不是,让我们继续。

    处理 IDisposable 的正确方法是丢弃它。我认为我们需要一个充分的理由不这样做。

    使用另一个锁:

    我认为最好的办法是使用Monitor 或其他锁,这样也可以简化代码。 ConcurrentDictionary 和其他框架类似乎采用了这种方法。

    你担心锁传递,但我不确定ReaderWriterLockSlim 是否解决了这个问题,唯一真正的解决方案是持有更少的锁并持有更少的时间。

    请勿丢弃:

    这需要一个理由。您能在这里展示所需的性能优势吗?

    如果你有一些这样的东西是长寿的,很好,不是所有的一次性用品都一样重(这不像你打开一个 word 文档),你可能会侥幸逃脱。正如已经指出的那样,在应用程序关闭之前处理所有这些毫秒有什么意义。我相信 IDisposable 的析构函数旨在处理未释放对象的情况,尽管您无法确定何时甚至是否调用它。

    但是,如果您有一个长期的应用程序并且该类有很多短期的用法,那么您可能会遇到麻烦。您正在对代码的使用做出假设,请注意。

    【讨论】:

    • “析构函数”是指终结器吗?这可能甚至没有必要,因为非托管资源本身将具有终结器,因此它们将执行 dispose 方法将调用的操作。我知道ReaderWriterLockSlim 只提供了许多密集读取操作的好处,对吧? Monitor 可能不会因为我正在尝试做的事情而表现不佳,但我希望将来安全。
    • C# 析构函数是 .NET 终结器。我确实指出他们是为自己做的,...我要删除那段。做出不处置的决定会影响您的一些假设(它会使编写一个寿命长的应用程序以短暂的方式使用大量这些对象变得更加棘手。正如 usr 指出的那样,它可能会吃掉所有的把手)。我会根据实际需要做出决定,而不是将来打样。
    • 是的,我认为某处有些歧义。简而言之,可能发生的最糟糕的事情是 GC 必须推迟对非托管资源的清理,因为它必须运行它们的终结器。至于烘焙假设,我想这一次我可以侥幸成功,正如 Hans 解释的那样,这种类并不适合在任何时候实例化,更不用说多次实例化了。
    • @toplel32 酷(我们总是在假设,这被称为编码,我在那里听起来有点戏剧化)。我认为这是明智的。我无法想象未来的代码会变成while(1) { var foo = new toplelResourceReader(); }
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-02
    • 2017-05-17
    • 1970-01-01
    • 1970-01-01
    • 2011-07-29
    • 1970-01-01
    相关资源
    最近更新 更多