Практическое использование System.WeakReference

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

19.08.2008 02:45:27
4 ОТВЕТА
РЕШЕНИЕ

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

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

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

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

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

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

47
19.08.2008 03:07:38
Да, это довольно изящно. Мы применили его код здесь, на работе, с некоторыми дополнениями для обработки других типов EventHandler (например, не универсальный EventHandler, PropertyChangedEventHandler и т. Д.). Это очень хорошо сработало для нас.
Judah Gabriel Himango 25.03.2009 14:19:43
Ссылка для реализации слабых обработчиков событий кажется мертвой. У кого-нибудь есть альтернативные ссылки, чтобы рекомендовать?
Pissu Pusa 29.01.2020 01:18:01
Judah Gabriel Himango 30.01.2020 02:02:38

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

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

    /* ... */
   }
13
20.08.2008 16:35:15

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

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

2
7.12.2008 12:04:04

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

  1. Вместо глобальных объектов, объявленных как статические : глобальные объекты объявляются как статические поля, и статические поля не могут быть GC-обработаны (сборка мусора) до тех пор, пока они AppDomainне GC-обработаны. Таким образом, вы рискуете исключения из памяти. Вместо этого мы можем обернуть глобальный объект в 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'ed в самом конце, даже после того, ThingsWrapperкак - т.е. GC'ed, когда AppDomainGC'ed.

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

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

    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
    }

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

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

0
25.08.2012 17:19:51