Практическое использование системы.Слабая ссылка

StackOverflow https://stackoverflow.com/questions/15593

  •  08-06-2019
  •  | 
  •  

Вопрос

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

Это было полезно?

Решение

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

Другое применение было бы в реализации слабых обработчиков событий.В настоящее время одним из основных источников утечек памяти в .NET-приложениях является забывание удалить обработчики событий.Например.

public MyForm()
{
    MyApplication.Foo += someHandler;
}

Видите проблему?В приведенном выше фрагменте MyForm будет сохранена в памяти навсегда, пока MyApplication находится в памяти.Создайте 10 MyForms, закройте их все, ваши 10 MyForms по-прежнему будут находиться в памяти, поддерживаемые обработчиком событий.

Введите WeakReference.Вы можете создать слабый обработчик событий, используя WeakReferences, чтобы someHandler был слабым обработчиком событий для MyApplication.Foo, таким образом устраняя ваши утечки памяти!

Это не просто теория.Дастин Кэмпбелл из блога DidItWith.NET опубликовал реализация слабых обработчиков событий использование системы.Слабая ссылка.

Другие советы

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

class Cache<TKey,TValue> : IEnumerable<KeyValuePair<TKey,TValue>>
{ Dictionary<TKey,WeakReference> dict = new Dictionary<TKey,WeakReference>();

   public TValue this[TKey key]
    { get {lock(dict){ return getInternal(key);}}
      set {lock(dict){ setInteral(key,value);}}     
    }

   void setInteral(TKey key, TValue val)
    { if (dict.ContainsKey(key)) dict[key].Target = val;
      else dict.Add(key,new WeakReference(val));
    } 


   public void Clear() { dict.Clear(); }

   /// <summary>Removes any dead weak references</summary>
   /// <returns>The number of cleaned-up weak references</returns>
   public int CleanUp()
    { List<TKey> toRemove = new List<TKey>(dict.Count);
      foreach(KeyValuePair<TKey,WeakReference> kv in dict)
       { if (!kv.Value.IsAlive) toRemove.Add(kv.Key);
       }

      foreach (TKey k in toRemove) dict.Remove(k);
      return toRemove.Count;
    }

    public bool Contains(string key) 
     { lock (dict) { return containsInternal(key); }
     }

     bool containsInternal(TKey key)
      { return (dict.ContainsKey(key) && dict[key].IsAlive);
      }

     public bool Exists(Predicate<TValue> match) 
      { if (match==null) throw new ArgumentNullException("match");

        lock (dict)
         { foreach (WeakReference weakref in dict.Values) 
            { if (   weakref.IsAlive 
                  && match((TValue) weakref.Target)) return true;
         }  
      }

       return false;
     }

    /* ... */
   }

Я использую слабую ссылку для сохранения состояния в миксинах.Помните, микшины статичны, поэтому, когда вы используете статический объект для привязки состояния к нестатическому, вы никогда не знаете, сколько времени это потребуется.Таким образом, вместо того, чтобы хранить Dictionary<myobject, myvalue> Я храню Dictionary<WeakReference,myvalue> чтобы миксер не затягивал процесс приготовления слишком долго.

Единственная проблема заключается в том, что каждый раз, когда я делаю доступ, я также проверяю наличие мертвых ссылок и удаляю их.Не то чтобы они причиняли кому-то вред, если, конечно, их не тысячи.

Есть две причины, по которым вы бы использовали WeakReference.

  1. Вместо глобальных объектов, объявленных как статические:Глобальные объекты объявляются как статические поля, а статические поля не могут быть обработаны GC'ed (сбор мусора) до тех пор, пока AppDomain является GC'ed.Таким образом, вы рискуете получить исключения из-за нехватки памяти.Вместо этого мы можем обернуть глобальный объект в WeakReference.Даже несмотря на то, что WeakReference сам по себе объявлен статическим, объект, на который он указывает, будет удален при нехватке памяти.

    В принципе, используйте wrStaticObject вместо того, чтобы staticObject.

    class ThingsWrapper {
        //private static object staticObject = new object();
        private static WeakReference wrStaticObject 
            = new WeakReference(new object());
    }
    

    Простое приложение для доказательства того, что статический объект собирает мусор, когда используется AppDomain.

    class StaticGarbageTest
    {
        public static void Main1()
        {
            var s = new ThingsWrapper();
            s = null;
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }
    }
    class ThingsWrapper
    {
        private static Thing staticThing = new Thing("staticThing");
        private Thing privateThing = new Thing("privateThing");
        ~ThingsWrapper()
        { Console.WriteLine("~ThingsWrapper"); }
    }
    class Thing
    {
        protected string name;
        public Thing(string name) {
            this.name = name;
            Console.WriteLine("Thing() " + name);
        }
        public override string ToString() { return name; }
        ~Thing() { Console.WriteLine("~Thing() " + name); }
    }
    

    Примечание из приведенных ниже выходных данных staticThing выполняется GC'ed в самом конце, даже после ThingsWrapper является - т. е.GC'ed, когда AppDomain является GC'ed.

    Thing() staticThing
    Thing() privateThing
    ~Thing() privateThing
    ~ThingsWrapper
    ~Thing() staticThing
    

    Вместо этого мы можем обернуть Thing в WeakReference.Как wrStaticThing может быть GC'ed, нам понадобится метод с отложенной загрузкой, который я опустил для краткости.

    class WeakReferenceTest
    {
        public static void Main1()
        {
            var s = new WeakReferenceThing();
            s = null;
            GC.Collect();
            GC.WaitForPendingFinalizers();
            if (WeakReferenceThing.wrStaticThing.IsAlive)
                Console.WriteLine("WeakReference: {0}", 
                    (Thing)WeakReferenceThing.wrStaticThing.Target);
            else 
                Console.WriteLine("WeakReference is dead.");
        }
    }
    class WeakReferenceThing
    {
        public static WeakReference wrStaticThing;
        static WeakReferenceThing()
        { wrStaticThing = new WeakReference(new Thing("wrStaticThing")); }
        ~WeakReferenceThing()
        { Console.WriteLine("~WeakReferenceThing"); }
        //lazy-loaded method to new Thing
    }
    

    Обратите внимание на приведенные ниже выходные данные, что wrStaticThing выполняется GC'ed при вызове потока GC.

    Thing() wrStaticThing
    ~Thing() wrStaticThing
    ~WeakReferenceThing
    WeakReference is dead.
    
  2. Для объектов, инициализация которых занимает много времени:Вы не хотите, чтобы объекты, для инициализации которых требуется время, были объединены в GC.Вы можете либо сохранить статическую ссылку, чтобы избежать этого (с минусами из приведенного выше пункта), либо использовать WeakReference.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top