2008-08-19 37 views
33

Tôi hiểu những gì System.WeakReference làm, nhưng những gì tôi dường như không thể nắm bắt là một ví dụ thực tế về những gì nó có thể hữu ích cho. Bản thân lớp học dường như là tôi, một bản hack. Dường như với tôi rằng có những phương tiện khác, tốt hơn để giải quyết một vấn đề mà một WeakReference được sử dụng trong các ví dụ tôi đã nhìn thấy. Ví dụ kinh điển về nơi bạn thực sự đã sử dụng một WeakReference là gì? Không phải chúng tôi đang cố gắng để có được xa hơn cách loại hành vi này và sử dụng lớp học này?Sử dụng thực tế của System.WeakReference

Trả lời

44

Một ví dụ hữu ích là những kẻ chạy cơ sở dữ liệu hướng đối tượng db4o. Ở đó, WeakReferences được sử dụng như một loại bộ nhớ cache ánh sáng: nó sẽ giữ cho các đối tượng của bạn trong bộ nhớ chỉ miễn là ứng dụng của bạn làm, cho phép bạn đặt một bộ nhớ cache thực trên đầu trang.

Sử dụng khác sẽ là trong việc triển khai trình xử lý sự kiện yếu. Hiện tại, một nguồn rò rỉ bộ nhớ lớn trong các ứng dụng .NET là quên loại bỏ các trình xử lý sự kiện. Ví dụ.

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

Xem sự cố? Trong đoạn mã trên, MyForm sẽ được lưu giữ trong bộ nhớ mãi mãi miễn là MyApplication vẫn còn trong bộ nhớ. Tạo 10 MyForms, đóng tất cả, 10 MyForms của bạn sẽ vẫn còn trong bộ nhớ, được giữ bởi trình xử lý sự kiện.

Nhập WeakReference. Bạn có thể xây dựng một trình xử lý sự kiện yếu bằng cách sử dụng WeakReferences để someHandler là một trình xử lý sự kiện yếu cho MyApplication.Foo, do đó sửa lỗi rò rỉ bộ nhớ của bạn!

Đây không chỉ là lý thuyết. Dustin Campbell từ blog DidItWith.NET đã đăng an implementation of weak event handlers bằng System.WeakReference.

+3

+1 cho mẹo với người tổ chức sự kiện, điều đó thật tuyệt vời !!!! –

+0

Vâng, nó khá tiện lợi. Chúng tôi đã áp dụng mã của anh ấy tại nơi làm việc, với một số bổ sung để xử lý các loại EventHandlers khác (ví dụ: EventHandler không chung, PropertyChangedEventHandler, v.v.). Nó làm việc rất tốt cho chúng tôi. –

12

tôi sử dụng nó để thực hiện một bộ nhớ cache nơi mục không sử dụng sẽ được tự động thu gom rác thải:

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

    /* ... */ 
    } 
2

Tôi sử dụng tham chiếu yếu để giữ trạng thái trong hỗn hợp. Hãy nhớ rằng, mixins là tĩnh, vì vậy khi bạn sử dụng một đối tượng tĩnh để đính kèm nhà nước vào một không tĩnh, bạn không bao giờ biết nó sẽ được yêu cầu bao lâu. Vì vậy, thay vì giữ một số Dictionary<myobject, myvalue> Tôi giữ một Dictionary<WeakReference,myvalue> để ngăn không cho mixin kéo các vật quá lâu.

Vấn đề duy nhất là mỗi khi tôi truy cập, tôi cũng kiểm tra các tài liệu tham khảo đã chết và xóa chúng. Không phải là họ làm tổn thương bất cứ ai, trừ khi có hàng ngàn, tất nhiên.

0

Có hai lý do khiến bạn sử dụng WeakReference.

  1. Thay vì đối tượng toàn cầu khai báo là tĩnh: Tác phẩm toàn cầu được khai báo là static fields và các lĩnh vực tĩnh không thể GC'ed (thu gom rác) cho đến khi AppDomain là GC'ed. Vì vậy, bạn có nguy cơ ngoại lệ bộ nhớ ngoài. Thay vào đó, chúng ta có thể bọc đối tượng chung trong một WeakReference. Mặc dù WeakReference chính nó được khai báo tĩnh, đối tượng nó trỏ đến sẽ được GC 'khi bộ nhớ thấp.

    Về cơ bản, sử dụng wrStaticObject thay vì staticObject.

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

    Ứng dụng đơn giản để chứng minh rằng đối tượng tĩnh được thu gom rác khi AppDomain là.

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

    Lưu ý từ đầu ra dưới đây staticThing được GC'ed vào cuối, ngay cả sau khi ThingsWrapper là - ví dụ: GC'ed khi AppDomain là GC'ed.

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

    Thay vào đó, chúng tôi có thể quấn Thing trong WeakReference. Như wrStaticThing có thể được GC'ed, chúng tôi sẽ cần một phương pháp lười biếng tải mà tôi đã bỏ ra cho ngắn gọn.

    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 
    } 
    

    Lưu ý từ đầu ra bên dưới wrStaticThing là GC 'khi GC được gọi.

    Thing() wrStaticThing 
    ~Thing() wrStaticThing 
    ~WeakReferenceThing 
    WeakReference is dead. 
    
  2. Đối với đối tượng là tốn nhiều thời gian để khởi tạo: Bạn không muốn đối tượng có thời gian consusming init được GC'ed. Bạn có thể giữ nguyên tham chiếu tĩnh để tránh điều đó (với khuyết điểm từ điểm trên) hoặc sử dụng WeakReference.

Các vấn đề liên quan