2012-10-11 64 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。这并不难。