关于.net:如何管理缓存的IDisposable对象?

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中,在那里它被称为对象池。他们的BorrowObjectReturnObject方法与我的示例中的方法匹配。


致(错误)引用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",以强调调用方不负责创建,因此不负责处理。