这是一个懒惰线程安全的通用多通道吗?

时间:2013-05-23 14:09:17

标签: c# multithreading design-patterns

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

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

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() {
    }
}

6 个答案:

答案 0 :(得分:1)

Dictionary并非设计为可由多个线程访问,并且您没有同步访问权限,因此,当同时访问时,它不会始终按预期工作。

最简单的解决方案是使用ConcurrentDictionary代替Dictionary

答案 1 :(得分:1)

我是多元概念的新手并质疑它的必要性。也就是说,您可以使用ConcurrentDictionary<T>进行相当大的改进,如下所示:

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<T>,GetOrAdd只会添加/返回一个,这意味着此并行添加的成本仅为懒散的懒惰,没有实例化它的价值。这就是为什么保留Lazy<T>对于使其正常工作至关重要的原因。请阅读this以获取更多信息。

答案 2 :(得分:1)

使用ConcurrentDictionary使字典线程安全。

答案 3 :(得分:1)

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

答案 4 :(得分:0)

Dictionary类不是线程安全的,您可以使用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() {
    }
}

答案 5 :(得分:0)

我在想,这不是一个更加精确的线程安全的Multiton实现,基于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()
    {
    }
}