.Net中弱字典的良好实现
在哪里可以找到内部使用弱引用的 IDictionary
的良好实现?
字典应该只保存对值的弱引用,并最终清除自身的死引用。
还是我应该自己写?
Where can I find good implementation of IDictionary
which uses weak references inside?
Dictionary should be holding only weak references to values and eventually clean up itself of dead references.
Or should I just write it myself?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
ConditionalWeakTable Class 使用弱键并尽快自动删除键/值条目因为表之外不存在对键的其他引用。
ConditionalWeakTable Class uses weak keys and automatically removes the key/value entry as soon as no other references to a key exist outside the table.
你需要自己写。它应该相对简单,实现
IDictionary
接口,然后将实际值存储为WeakReferences
。然后,您可以使用TryGetTarget
检查添加/选择上的值,看看它们是否仍然存在。You'll need to write it yourself. It should be relatively straight forward, implementing the
IDictionary<T,T>
interface and then storing the actual values asWeakReferences<T>
. You can then check the values on add/select usingTryGetTarget
to see if they're still alive.简单地保存 WeakReference 对象字典的一个问题是,除非枚举整个字典,否则无法从字典中删除目标超出范围的任何 WeakReference 对象。
如果 WeakReference 可以包含一个委托,当主要目标超出范围时将调用该委托,这将很有帮助。据我所知,没有办法做到这一点。如果您不介意向存储在“弱字典”中的对象添加另一个字段和一些代码,我建议创建我所说的“Finasposer”对象,其唯一字段是 MethodInvoker;当释放时,MethodInvoker 应该被清空;终结器应该 Interlocked.Exchange() 将 MethodInvoker 设置为 null,并且(如果其旧值非 null)调用它。要写入字典的对象应创建一个新的 Finasposer 对象,并带有一个委托,该委托将在方便时从字典中删除键。
请注意,终结器和由此调用的任何委托都不应直接操作字典,也不应执行任何需要获取锁的操作。如果 Finasposer 持有委托,则在 Finalize 执行时,该委托本身保证有效,但附加到委托的对象以及由此引用的任何对象可能处于意外状态。然而,对于 Finasposer 调用的方法来说,将超出范围的对象的引用添加到链接列表中应该是安全的。 Dictionary 的 Add、Remove 和其他方法可以轮询链表,以查看其中是否有任何 WeakReference 已死亡并需要清除。
One problem with simply holding a dictionary of WeakReference objects is that there's no way, short of enumerating the entire dictionary, of removing from the Dictionary any WeakReference objects whose targets go out of scope.
It would be helpful if a WeakReference could include a delegate which would be invoked when the primary target went out of scope. As far as I know, there's no way to do that. If you don't mind adding another field and a little code to the objects you're storing within your "weak dictionary", I'd suggest creating what I call a "Finasposer" object, whose sole field is a MethodInvoker; when disposed, the MethodInvoker should be nulled out; the finalizer should Interlocked.Exchange() the MethodInvoker to null and--if its old value was non-null--invoke it. The object to be written in the dictionary should create a new Finasposer object, with a delegate that will cause the key to be removed from the dictionary when convenient.
Note that the neither the finalizer nor any delegate invoked thereby should never directly manipulate the dictionary, nor do anything that would require acquiring a lock. If the Finasposer holds a delegate, that delegate itself is guaranteed to be valid when Finalize executes, but the object attached to the delegate, and any objects referenced thereby, may be in unexpected states. It should be safe, however, for the Finasposer-called method to add to a linked list a reference to the object that went out of scope. The Dictionary's Add, Remove, and other methods could poll the linked list to see if any of the WeakReferences therein had died and needed to be cleaned out.
这将不会出现其他解决方案的性能问题。
(它不依赖于每个请求调用“shrink”方法来手动删除死对象。这些“shrink”方法必须在每次调用时循环遍历每个项目。我确实有一个“shrink” " 方法,但只有在枚举项目时才会调用它。)
问题的关键是使用“holder”对象作为 ConditionalWeakTable 中的值,这样当键被删除时,holder 的终结器就会触发,这会从密钥的“活动列表”中删除该密钥。
我测试了这个并且它有效。
This will work without the performance problems of the other solutions.
(It doesn't rely on a "shrink" method being called upon every request in order to manually get rid of dead objects. And these "shrink" methods would have to loop through every item on every call. I do have a "shrink" method, but it is only called when the items would be enumerated anyway.)
The key to the problem is to use a "holder" object as a value in the ConditionalWeakTable, so that when the key gets dropped, the holder's finalizer will trigger, which removes the key from the "active list" of keys.
I tested this and it works.
似乎所有现有的答案要么:
调用我实现的版本,该版本将弱引用应用于字典values,立即删除垃圾收集值的条目。
仓库: bhaeussermann/weak-dictionary
NuGet 包: BernhardHaus.Collections.WeakDictionary
文章: 在 .NET 中创建弱字典
实现注意:
ConditionalWeakTable
类拥有对其键的弱引用,但我们希望弱引用位于值上。因此,我们使用字典的值作为ConditionalWeakTable
的键。ConditionalWeakTable
会删除相关条目,这会导致值也被垃圾回收(假设没有其他对该值的引用)。这会导致调用值对象的解构函数。我们利用这一点来立即从内部字典中删除相应的条目。下面是一个 NUnit 测试,显示一旦条目的值不再有任何引用,该条目就会被删除。
这已在 .NET Framework 和 .NET Core 上进行了测试。请注意,为了看到它与 .NET Core 一起工作,需要打开编译器优化,并且不得附加调试器。请参阅这篇文章。
It seems that all existing answers either:
I've implemented a version that applies weak references to the dictionary values, removing the entries of garbage collected values immediately.
Repo: bhaeussermann/weak-dictionary
NuGet package: BernhardHaus.Collections.WeakDictionary
Article: Creating a weak dictionary in .NET
Implementation notes:
ConditionalWeakTable
class holds a weak reference to its key, but we want the weak reference to be on the value. Therefore we use our dictionary's values as the keys of theConditionalWeakTable
.ConditionalWeakTable
removes the relevant entry and this causes the value to be garbage collected as well (assuming there are no other references to the value). This causes the deconstructor of the value object to be invoked. We leverage this in order to immediately remove the corresponding entry from the internal dictionary.Here's an NUnit test that shows that an entry is removed once its value no longer has any references to it.
This has been tested on both .NET Framework and .NET Core. Note that in order to see it work with .NET Core compiler optimizations need to be switched on, and the debugger must not be attached. See this post.
这是我的并发弱(值)字典版本:
以及一个测试,证明对值的引用实际上被丢弃:
一些注释:
This is my version of a concurrent weak (value) dictionary:
and a test proving that reference to value is actually discarded:
Some notes:
对值进行弱引用是一回事,但我发现字典键也可能是内存泄漏的根源。这是一个带有 WeakReference 对键的简单实现:
It is one thing to have WeakReferences to values, but I found that Dictionary keys can also be a source of memory leaks. Here is a bare bones implementation with WeakReference to keys:
如果无法使用恒等比较,则 ConditionalWeakTable 不是一个选项。
在这种情况下,我敢于建议我们的实施
WeakTable.cs,
以及我们在博客中的描述
WeakTable。
If identity comparison cannot be used then ConditionalWeakTable is not an option.
In this case I dare to suggest our implementation
WeakTable.cs,
and our description in the blog
WeakTable.