How to manage IDisposable Objects that are cached?
我有一个创建成本很高的对象,它使用一些非托管资源,这些资源在使用后必须显式释放,因此实现IDisposable()。我想要一个缓存,例如这些昂贵的资源,这样创建成本就可以最小化,但是我很难知道如何处理这种处理。
如果使用对象的方法负责释放,那么我最终会在缓存中释放实例,然后必须重新创建这些实例,从而破坏缓存点。如果我不在使用它们的方法中释放对象,那么它们就永远不会被释放。我以为我可以在从缓存中取出它们时将它们处理掉,但最后我可能会处理一个仍被某个方法使用的实例。
让它们超出范围并由垃圾收集器收集,然后在此时释放资源是否有效?这感觉不对,而且反对他们被抛弃的想法…
一次性物品总是需要有一个明确的所有者负责处理它们。然而,这并不总是创建它们的对象。此外,所有权可以转让。
意识到这一点,解决方案变得显而易见。不要丢弃,回收!您不仅需要一种从缓存获取资源的方法,还需要一种返回资源的方法。此时,缓存又是所有者,可以选择保留资源以供将来使用或处置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public interface IDisposableItemCache<T> : IDisposable where T:IDisposable { /// <summary> /// Creates a new item, or fetches an available item from the cache. /// </summary> /// <remarks> /// Ownership of the item is transfered from the cache to the client. /// The client is responsible for either disposing it at some point, /// or transferring ownership back to the cache with /// <see cref="Recycle"/>. /// </remarks> T AcquireItem(); /// <summary> /// Transfers ownership of the item back to the cache. /// </summary> void Recycle(T item); } |
编辑:我刚注意到这个想法也存在于Spring中,在那里它被称为对象池。他们的
致(错误)引用Raymond Chen:没有过期策略的每个缓存都是一个漏洞
因此,设置一个清除缓存过期策略,并让缓存将其作为正常情况处理。这仍然需要处理应用程序关闭。
如果非托管资源归进程所有,则可以让进程在关闭时释放它们。
如果非托管资源不属于进程,则需要检测关闭并明确地处置缓存元素。
如果无法可靠地检测到进程关闭,并且托管资源昂贵,则非托管资源不昂贵,请将托管资源与非托管资源分开,并让缓存只保留托管资源。
当非托管资源昂贵,需要缓存,并且它们不属于进程,并且您无法可靠地检测到进程关闭,并且您负担不起泄漏,那么您的问题就无法解决。
首先,包装本机资源的类型应该是可终结的,而不仅仅是一次性的。更好的是,使用safehandle包装本机资源。
除非有人明确负责说他们已经完成了这个项目并且可以处理它,否则我认为你最好让GC来处理它。但请注意,它必须是可完成的,否则GC将不会对其进行第二次浏览。
您可以将非托管资源与托管实例分离,并使用缓存管理器来保存一组非托管资源。托管对象将尝试从缓存管理器获取非托管资源的实例,该实例将从缓存创建一个实例或提供一个空闲实例,并在处置时将其返回到缓存管理器(而不是自行处置)。当缓存管理器认为有必要时,它将是唯一负责分配和释放非托管资源的对象。
您可以使用类工厂和IDisposable来解决这个问题。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | public class CachedObject : IDisposable { private int mRefCount; private CachedObject(int something) { mRefCount = 1; } public static CachedObject CreateObject(int something) { CachedObject obj = LookupInCache(something); if (obj != null) Interlocked.Increment(ref obj.mRefCount); else obj = new CachedObject(something); return obj; } private static CachedObject LookupInCache(int something) { CachedObject obj = null; // Implement cache lookup here, don't forget to lock //.. return obj; } public void Dispose() { int cnt = Interlocked.Decrement(ref mRefCount); if (cnt == 0) { // Remove from cache // } } } |
对象应该由创建它的类释放。由于调用方没有在缓存中创建这些项,因此它们也没有处理这些项的业务。
我想确保您的工厂方法的命名类似于"getClass",而不是"createClass",以强调调用方不负责创建,因此不负责处理。