首页 文章

如何管理缓存的IDisposable对象?

提问于
浏览
10

我有一个创建成本昂贵的对象,它使用一些必须在完成时显式释放的非托管资源,因此实现IDisposable() . 我想要一个缓存,例如这些昂贵的资源,以便最大限度地降低创建成本,但我很难知道如何处理这些处理 .

如果使用这些对象的方法负责处理,那么我最终会在缓存中放置已处理的实例,然后必须重新创建实例,从而破坏缓存的重点 . 如果我没有在使用它们的方法中处理对象,那么它们永远不会被处理掉 . 我认为当它们从缓存中取出时我可以处理它们,但是我可能最终会处理一个仍被方法使用的实例 .

让它们超出范围并由垃圾收集器收集并在那时释放资源是否有效?这感觉不对,并反对他们是一次性的想法......

6 回答

  • 3

    一次性物品总是需要有一个明确的所有者负责处理它们 . 但是,这并不总是创建它们的对象 . 此外,所有权可以转移 .

    意识到这一点,解决方案变得显而易见 . Don't dispose, recycle! 您不仅需要一种从缓存中获取资源的方法,还需要一种返回资源的方法 . 此时缓存是所有者,并且可以选择保留资源以供将来使用或处置它 .

    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);
    
       }
    

    edit: 我刚刚注意到这个想法在Spring中也存在,它被称为object pool . 他们的 BorrowObjectReturnObject 方法与我的示例中的方法匹配 .

  • 1

    To(mis-)引用Raymond Chen:没有过期策略的每个缓存都是泄漏

    因此,设置一个清除缓存过期策略,并让缓存将它们作为正常情况进行处理 . 这仍然会使应用程序关闭处理 .

    如果您的非托管资源由流程拥有,您可以让流程在关闭时释放它们 .

    如果进程拥有非托管资源,则需要检测shutdown并明确地释放缓存的元素 .

    如果您无法可靠地检测进程关闭,并且托管资源很昂贵,则非托管资源不是,将托管与非托管资源分开,并让缓存仅保留托管资源 .

    当非托管资源很昂贵,因此它们需要缓存,而且它们不属于流程所有,并且您无法可靠地检测到流程关闭而且您无法承受泄漏,那么您的问题就无法解决 .

  • 4

    首先,包装本机资源的类型应该是最终的,而不仅仅是一次性的 . 更好的是,使用SafeHandle来包装本机资源 .

    除非某人明确负责说他们已完成该项目并且可以处理它,否则我认为你最好让GC处理它 . 请注意,它必须是可以最终确定的,否则GC不会给它第二眼 .

  • 2

    您可以将非托管资源与托管实例分离,并使用缓存管理器来保存一组非托管资源 . 托管对象将尝试从缓存管理器获取非托管资源的实例,该实例将创建一个或从缓存中提供一个空闲实例,并在处置时将其返回到缓存管理器(而不是自己处理) . . 缓存管理器将是唯一负责的对象,用于在看到必要时分配和释放非托管资源 .

  • 4

    您可以使用类工厂和IDisposable解决此问题 . 例如:

    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
          // 
        }
      }
    }
    
  • 0

    对象应由创建它的类处理 . 由于您的调用者尚未在缓存中创建项目,因此他们也没有业务处理它们 .

    我想确保您的工厂方法的名称类似于“GetClass " rather than " CreateClass”,以强调调用者不负责创建,因此不负责处理 .

相关问题