有人可以检查这个版本的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() {
}
}
答案 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()
{
}
}