我有一个创建成本很高的对象,它使用一些必须在完成时显式释放的非托管资源,因此实现IDisposable()。我想要一个缓存,例如这些昂贵的资源,以便最大限度地降低创建成本,但我很难知道如何处理这些处理。
如果使用这些对象的方法负责处理,那么我最终会在缓存中处理已处置的实例,然后必须重新创建实例,从而破坏缓存的点。如果我没有在使用它们的方法中处理对象,那么它们永远不会被处理掉。我认为当它们从缓存中取出时我可以处理它们,但是我最终可能会处理一个仍被方法使用的实例。
让它们超出范围并被垃圾收集器收集并在那时释放资源是否有效?这感觉不对,并反对他们是一次性的想法......
答案 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。他们的BorrowObject
和ReturnObject
方法与我示例中的方法相匹配。
答案 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 ”,以强调调用方是不负责创作,因此不负责处置。