Как управлять изолированными объектами IDisposable?

У меня есть объект, который стоит создавать, который использует некоторые неуправляемые ресурсы, которые должны быть явно освобождены, когда это сделано, и таким образом реализовать IDisposable(). Я хотел бы, например, кеш этих дорогостоящих ресурсов, чтобы минимизировать затраты на создание, но у меня возникли проблемы с пониманием того, как справиться с этим.

Если методы, использующие объекты, несут ответственность за удаление, я заканчиваю располагаемыми экземплярами в кеше, которые затем нужно воссоздать, победив точку кеша. Если я не размещаю объекты в методах, которые их используют, они никогда не удаляются. Я думал, что могу избавиться от них, когда их вынут из кеша, но потом я мог бы удалить экземпляр, который все еще используется методом.

Действительно ли это просто позволить им выйти из сферы действия и собираться сборщиком мусора и освобождать ресурсы в этот момент? Это кажется неправильным и противоречит идее их использования...

Ответ 1

Одноразовые объекты всегда должны иметь четкого владельца, который несет ответственность за их удаление. Однако это не всегда объект, который их создал. Кроме того, право собственности может быть передано.

Понимая это, решение становится очевидным. Не утилизируйте, перерабатывайте! Вам нужен не только способ получить ресурс из кеша, но и способ его возврата. В этот момент кеш является владельцем снова и может выбрать сохранение ресурса для будущего использования или его распоряжения.

   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, где она называется пул объектов. Их методы BorrowObject и ReturnObject соответствуют методам в моем примере.

Ответ 2

To (mis-) quote Raymond Chen: каждый кеш без политики истечения срока действия - это утечка

Итак, установите прозрачную политику истечения срока действия кэша и дайте кешу распоряжаться ими как обычным делом. Это по-прежнему не работает.

Если ваш неуправляемый ресурс принадлежит процессу, вы можете позволить процессу освободить их при завершении работы.

Если неуправляемые ресурсы rustources не, принадлежащие процессу, вам необходимо обнаружить выключение и явно Dispose кешированные элементы.

Если вы не можете надежно обнаружить завершение процесса, а управляемые ресурсы - дорогостоящие, неуправляемые не будут разделены управляемыми из неуправляемых ресурсов, и пусть кеш хранит только управляемые.

Когда неуправляемые ресурсы являются дорогостоящими, поэтому они нуждаются в кешировании, и они не принадлежат процессу, и вы не можете надежно определить процесс завершения работы, и вы не можете позволить себе их протекать, тогда ваша проблема не может быть решена.

Ответ 3

Прежде всего, тип, который обертывает собственные ресурсы, должен быть финализирован, а не просто одноразовым. Еще лучше, SafeHandle, чтобы обернуть собственные ресурсы.

Если кто-то явно не несет ответственности за высказывание, что они сделаны с этим предметом, и его можно утилизировать, я думаю, что вам лучше позволить GC заботиться об этом. Обратите внимание, что он должен быть финализирован, хотя в противном случае GC не даст ему второго взгляда.

Ответ 4

Вы можете отделить неуправляемые ресурсы от управляемого экземпляра и использовать диспетчер кэша для хранения набора неуправляемых ресурсов. Управляемый объект попытается получить экземпляр неуправляемого ресурса из диспетчера кэша, который либо создаст один, либо предоставит один свободный экземпляр из кэша и вернет его в диспетчер кэша (вместо его удаления) в момент его удаления, Менеджер кэша будет единственным ответственным объектом для выделения и освобождения неуправляемых ресурсов, когда он сочтет это необходимым.

Ответ 5

Вы можете решить это с помощью класса factory и 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
      // 
    }
  }
}

Ответ 6

Объект должен быть удален классом, который его создает. Так как ваши вызывающие лица не создали элементы в кеше, они также не имеют возможности распоряжаться ими.

Я хотел бы убедиться, что ваш метод factory называется чем-то вроде "GetClass", а не "CreateClass", чтобы подчеркнуть, что вызывающий абонент не несет ответственности за создание и, следовательно, не для удаления.