Сведения о вопросе

Faridun

04:58, 29th August, 2020

Теги

Практическое применение System.WeakReference

Просмотров: 482   Ответов: 4

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



  Сведения об ответе

ASSembler

20:28, 2nd August, 2020

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

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

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

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

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

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


  Сведения об ответе

park

00:19, 18th August, 2020

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

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;
     }

    /* ... */
   }


  Сведения об ответе

padenie

13:36, 10th August, 2020

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

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


  Сведения об ответе

DINO

16:02, 7th August, 2020

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

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

    В принципе, используйте 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 объед в самом конце, даже после ThingsWrapper это - т. е. GC объед когда AppDomain является GC объед.

    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 объед когда нить GC вызывается.

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


Ответить на вопрос

Чтобы ответить на вопрос вам нужно войти в систему или зарегистрироваться