如何管理缓存的IDisposable对象?

时间:2009-02-20 11:46:24

标签: .net caching idisposable

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

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

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

6 个答案:

答案 0 :(得分:4)

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

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

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

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

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

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

答案 1 :(得分:4)

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

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

   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中也存在,它被称为object pool。他们的BorrowObjectReturnObject方法与我示例中的方法相匹配。

答案 2 :(得分:3)

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

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

答案 3 :(得分:2)

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

答案 4 :(得分:1)

您可以使用类工厂和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
      // 
    }
  }
}

答案 5 :(得分:0)

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

我想确保您的工厂方法的名称类似于“Get Class ”,而不是“Create Class ”,以强调调用方是不负责创作,因此不负责处置。