【问题标题】:Is this a lazy thread safe Generic multiton?这是一个惰性线程安全的通用多线程吗?
【发布时间】:2013-05-23 14:09:17
【问题描述】:

有人可以检查这个版本的 Generic Multiton 是否是惰性和线程安全的?

我根据Jon Skeet's version of a lazy thread safe Singleton、O'Reilly 的 C# 设计模式和 Multiton 的维基百科 C# 版本对其进行了编码。

public sealed class Multiton<T> where T : class, new() {
    private static readonly Dictionary<object, Lazy<T>> _instances = new Dictionary<object, Lazy<T>>();
    public static T GetInstance(object key) {
        Lazy<T> instance;
        if (!_instances.TryGetValue(key, out instance)) {
            instance = new Lazy<T>(() => new T());
            _instances.Add(key, instance);
        }
        return instance.Value;
    }
    private Multiton() {
    }
}

【问题讨论】:

  • Isn't a mutliton just a singleton dictionary.
  • @DanielHilgarth 为什么?它不是要求最好的方法来改进它。它是在询问代码是否可以正常工作。
  • @Servy:他要求进行代码审查
  • @DanielHilgarth 不,代码审查几乎是在说,“那么我的代码怎么样,可以做些什么来使它变得更好”,而这是询问特定的、范围狭窄的属性程序和关于该财产是否存在的客观分析。完全适合 SO。

标签: c# multithreading design-patterns


【解决方案1】:

字典类不是线程安全的,你可以使用 ConcurrentDictionary 自动检查元素是否在字典中并从字典中添加/获取值:

public sealed class Multiton<T> where T : class, new() {
    private static readonly ConcurrentDictionary<object, Lazy<T>> _instances = new ConcurrentDictionary<object, Lazy<T>>();

    public static T GetInstance(object key) {
        Lazy<T> instance = _instances.GetOrAdd(key, k => new Lazy<T>(() => new T()));
        return instance.Value;
    }

    private Multiton() {
    }
}

【讨论】:

  • 请务必注意,使用 Lazy&lt;T&gt; 对这个架构至关重要,因为 GetOrAdd 可能会为同一个键多次运行其委托(但丢弃其中一个值)的可能性很小.使用 Lazy&lt;T&gt; 可以防止这个问题,因为唯一丢弃的是 Lazy,而不是您真正想要单独实例化的 T。
  • @Rafael 但是字典不是懒惰构建的,所以,它不是完全懒惰的,对吧。
【解决方案2】:

Dictionary 不是为多线程访问而设计的,而且您没有同步访问,所以不,当同时访问时,它不会总是按预期工作。

最简单的解决方案是使用ConcurrentDictionary 而不是Dictionary

【讨论】:

    【解决方案3】:

    使用ConcurrentDictionary 使您的字典线程安全。

    【讨论】:

      【解决方案4】:

      我是多吨概念的新手,并质疑它的必要性。也就是说,您可以通过使用ConcurrentDictionary&lt;T&gt; 来显着改善这一点,如下所示:

      public sealed class Multiton<T> where T : class, new() {
          private static readonly ConcurrentDictionary<object, Lazy<T>> _instances = 
              new ConcurrentDictionary<object, Lazy<T>>();
          public static T GetInstance(object key) {
              return _instances.GetOrAdd(key, k=>new Lazy<T>(() => new T())).Value;
          }
          private Multiton() {
          }
      }
      

      这很好,因为如果同时调用 GetOrAdd,即使有机会在同一个键上生成两个 Lazy&lt;T&gt;s,但只有一个会被 GetOrAdd 添加/返回,这意味着此并行添加的成本只是一次性的没有实例化它的值的懒惰。这就是为什么保留Lazy&lt;T&gt; 对于使其正常工作至关重要的原因。阅读this 了解更多信息。

      【讨论】:

        【解决方案5】:

        Dictionnary 的访问未同步。你应该锁定它:

            public static T GetInstance(object key) {
                lock (_instances) {   
                   Lazy<T> instance;
                   if (!_instances.TryGetValue(key, out instance)) {
                       instance = new Lazy<T>(() => new T());
                       _instances.Add(key, instance);
                   }
                }
                return instance.Value;
            }
        

        【讨论】:

        • 您不应该在锁内访问instance.Value。它已经被正确同步,因此不需要,并且在您等待访问 Lazy 的值时,无需阻止其他线程访问字典。
        • 我试图不使用 Lazy 类型的锁定机制
        【解决方案6】:

        我在想,这不是基于 Jon Skeet 的原始惰性线程安全单例更准确地实现线程安全多例吗?

        要完全惰性,静态字典需要惰性,这是原始单例的类比。字典成员也需要偷懒处理unlikey but possible double instantiation issue

        这是我建议的一个完全通用的实现。

        public sealed class Multiton<TKey, TInstance>
            where TInstance : class, new()
        {
            private static readonly
                Lazy<ConcurrentDictionary<TKey, Lazy<TInstance>>> dic =
                    new Lazy<ConcurrentDictionary<TKey, Lazy<TInstance>>>(
                        () => new ConcurrentDictionary<TKey, Lazy<TInstance>>());
        
            public static TInstance Instance(TKey key)
            {
                return dic.Value.GetOrAdd(
                    key, 
                    k => new Lazy<TInstance>(() => new TInstance())).Value;
            }
        
            private Multiton()
            {
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-08-03
          • 1970-01-01
          相关资源
          最近更新 更多