2012-10-11 107 views
3

可能重複:
C# Threading & Blocking線程安全用更少的鎖定

我試圖有效地確定哪種方法比較好:

目前,我有一個單一實例暴露以延遲加載方式加載的實體。我列出了三種方法,每種方法都有一些優點。第一種方法完全依靠雙重鎖定模式來確保線程安全。第二種方法不使用鎖定,但在比賽的情況下它有雙重加載的潛力。第三種方法確實使用了我非常喜歡的解決方案。 (System.Lazy)。

出於某種原因,我覺得第二種方法(System.Thread.InterLocked)有問題,但我不能指出它。是否有理由相互支持一種方法?我在之前的一篇文章中提到過,我覺得第三種選擇是從現在開始的方式。

我將代碼剝離到準系統,以便能夠解釋設計。

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace TPLDemo 
{ 
    public class SomeEntity 
    { 
    } 

    public class MultiThreadedManager 
    { 
    private static readonly System.Lazy<MultiThreadedManager> instance = new Lazy<MultiThreadedManager>(() => { return new MultiThreadedManager(); }); 
    private readonly object _syncRoot = new object(); 
    private List<SomeEntity> _inMemoryEntities = null; 
    private List<SomeEntity> _inMemoryEntitiesUsingLockFreeApproach = null; 
    private System.Lazy<List<SomeEntity>> _inMemoryUsingLazy = new Lazy<List<SomeEntity>>(() => { return MultiThreadedManager.Instance.LoadFromSomewhere(); }); 

    public static MultiThreadedManager Instance 
    { 
     get { return instance.Value; } 
    } 


    public IEnumerable<SomeEntity> LazyEntities 
    { 
     get 
     { 
     return _inMemoryUsingLazy.Value; 
     } 
    } 

    public IEnumerable<SomeEntity> LocklessEntities 
    { 
     get 
     { 
     if (_inMemoryEntitiesUsingLockFreeApproach == null) 
     { 
      do 
      { 
      // Is it possible multiple threads hit this at the same time? 
      } while (System.Threading.Interlocked.CompareExchange<List<SomeEntity>>(ref    _inMemoryEntitiesUsingLockFreeApproach, this.LoadFromSomewhere(), null) != null); 
     } 

     return _inMemoryEntitiesUsingLockFreeApproach; 
     } 
    } 


    /// <summary> 
    /// This is thread safe but it involved some locking. 
    /// </summary> 
    public IEnumerable<SomeEntity> Entities 
    { 
     get 
     { 
     if (_inMemoryEntities == null) 
     { 
      lock (_syncRoot) 
      { 
      if (_inMemoryEntities == null) 
      { 
       List<SomeEntity> list = this.LoadFromSomewhere(); 
       _inMemoryEntities = list; 
      } 
      } 
     } 

     return _inMemoryEntities; 
     } 
    } 

    private List<SomeEntity> LoadFromSomewhere() 
    { 
     return new List<SomeEntity>(); 
    } 

    public void ReloadEntities() 
    { 
     // This is sufficient becasue any subsequent call will reload them safely. 
     _inMemoryEntities = null; 

     // This is sufficient becasue any subsequent call will reload them safely. 
     _inMemoryEntitiesUsingLockFreeApproach = null; 

     // This is necessary becasue _inMemoryUsingLazy.Value is readonly. 
     _inMemoryUsingLazy = new Lazy<List<SomeEntity>>(() => { return MultiThreadedManager.Instance.LoadFromSomewhere(); }); 
    } 
    } 
} 
+0

爲什麼在你的第一篇文章中沒有回答(使用'System.Lazy ',這就是它的好處)? –

回答

0

第三個選項(Lazy)允許您配置它的行爲方式。你可以使它像(1)或者像(2)一樣運行。

在任何情況下,一旦它被加載,它不需要在內部鎖定或互鎖,從而使您回到加載的Value

所以通過一切手段去System.Lazy

但有一個令人討厭的事情:如果工廠功能失敗,則每次訪問Value屬性時都會存儲並拋出異常。這意味着這個Lazy實例沒有被破壞。你不能再試。這意味着暫時失敗(網絡錯誤,...)可能會永久取消應用程序,直到手動重新啓動。

如果在MS Connect上抱怨過這個問題,但它是有意設計的。

我的解決方案是寫我自己的Lazy。這並不難。