3

可能重复:
C# 线程和阻塞

我正在尝试有效地确定哪种方法更好:

目前,我有一个单例实例,它公开了以延迟加载方式加载的实体。我列出了三种方法,每种方法都有一些优点。第一种方法完全依赖双锁模式来确保线程安全。第二种方法不使用锁定,但它有可能在比赛中双重加载。第三种方法确实使用了我非常喜欢的解决方案。(系统。懒惰)。

出于某种原因,我觉得第二种方法(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(); });
    }
  }
}
4

1 回答 1

0

第三个选项 ( Lazy) 允许您配置它的行为方式。您可以使其行为类似于 (1) 或 (2)。

在任何情况下,一旦它被加载,它就不需要在内部锁定或互锁以将加载的Value.

所以一定要争取System.Lazy

但是有一件令人讨厌的事情:如果工厂函数失败,则每次Value访问属性时都会存储并抛出异常。这意味着这个Lazy实例没有被破坏。你永远不能重试。这意味着暂时性故障(网络错误,...)可能会永久关闭您的应用程序,直到手动重新启动它。

如果在 MS Connect 上对此有所抱怨,但这是设计使然。

我的解决方案是编写自己的Lazy. 这并不难。

于 2012-10-12T00:01:54.870 回答