Как управлять объектами IDisposable, которые кэшируются? - PullRequest
10 голосов
/ 20 февраля 2009

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

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

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

Ответы [ 6 ]

4 голосов
/ 20 февраля 2009

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

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

   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 соответствуют методам в моем примере.

4 голосов
/ 20 февраля 2009

Цитировать (неправильно) Раймонда Чена: Каждый кеш без политики истечения срока действия - это утечка

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

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

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

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

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

3 голосов
/ 20 февраля 2009

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

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

2 голосов
/ 20 февраля 2009

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

1 голос
/ 20 февраля 2009

Вы можете решить это с помощью фабрики классов и 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 голосов
/ 20 февраля 2009

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

Я бы хотел удостовериться, что ваш фабричный метод назван как «Get Class », а не «Create Class », чтобы подчеркнуть, что вызывающая сторона не несет ответственности для создания и, следовательно, не для уничтожения.

...