我有以下助手类(简化):
public static class Cache
{
private static readonly object _syncRoot = new object();
private static Dictionary<Type, string> _lookup = new Dictionary<Type, string>();
public static void Add(Type type, string value)
{
lock (_syncRoot)
{
_lookup.Add(type, value);
}
}
public static string Lookup(Type type)
{
string result;
lock (_syncRoot)
{
_lookup.TryGetValue(type, out result);
}
return result;
}
}
Add
将在应用程序中大约调用10/100次,Lookup
将被许多线程调用,数千次。我想要的是摆脱读锁。
在这种情况下,你通常如何摆脱读锁?
我有以下想法:
要求_lookup
在应用程序开始运行之前保持稳定。可以从Attribute
构建。这是通过分配属性的静态构造函数自动完成的。要求上述内容需要我查看所有可能具有该属性的类型并调用RuntimeHelpers.RunClassConstructor
这是一项昂贵的操作;
转到COW语义。
public static void Add(Type type, string value)
{
lock (_syncRoot)
{
var lookup = new Dictionary<Type, string>(_lookup);
lookup.Add(type, value);
_lookup = lookup;
}
}
(在lock (_syncRoot)
方法中删除Lookup
。)问题是这会占用不必要的内存量(这可能不是问题)而且我可能会_lookup
1}} volatile,但我不确定应该如何应用它。 (John Skeets' comment here gives me pause.)
ReaderWriterLock
。我认为这会让事情变得更糟,因为被锁定的区域很小。建议非常欢迎。
更新
缓存的值是不可变的。
答案 0 :(得分:1)
要完全删除锁(略微不同,然后“锁定自由”,其中几乎消除了锁,其余的被巧妙地替换为Interlocked指令),您需要确保您的字典是不可变的。如果字典中的项不是不可变的(并且因此它们拥有自己的锁),您可能不必担心在字典级别上锁定。
如果您可以使用新的4.0集合 - ConcurrentDictionary符合您的条件(请参阅http://msdn.microsoft.com/en-us/library/dd997305.aspx和http://blogs.msdn.com/b/pfxteam/archive/2010/01/26/9953725.aspx)。
答案 1 :(得分:0)
此刻的工作,没有什么优雅,想出了这个(未经测试的)
public static class Cache
{
private static readonly object _syncRoot = new object();
private static Dictionary<Type, string> _lookup = new Dictionary<Type, string>();
public static class OneToManyLocker
{
private static readonly Object WriteLocker = new Object();
private static readonly List<Object> ReadLockers = new List<Object>();
private static readonly Object myLocker = new Object();
public static Object GetLock(LockType lockType)
{
lock(WriteLocker)
{
if(lockType == LockType.Read)
{
var newReadLocker = new Object();
lock(myLocker)
{
ReadLockers.Add(newReadLocker);
}
return newReadLocker;
}
foreach(var readLocker in ReadLockers)
{
lock(readLocker) { }
}
return WriteLocker;
}
}
public enum LockType {Read, Write};
}
public static void Add(Type type, string value)
{
lock(OneToManyLocker.GetLock(OneToManyLocker.LockType.Write))
{
_lookup.Add(type, value);
}
}
public static string Lookup(Type type)
{
string result;
lock (OneToManyLocker.GetLock(OneToManyLocker.LockType.Read))
{
_lookup.TryGetValue(type, out result);
}
return result;
}
}
你需要对读取锁定器进行某种清理,但是应该是线程安全的,允许一次多次读取,同时锁定写入,除非我完全遗漏了某些东西
答案 2 :(得分:0)
或者:
不要使用普通锁,如果查询速度很快,请使用自旋锁(字典不是)。
如果不是这样,请使用http://msdn.microsoft.com/en-us/library/system.threading.readerwriterlock.aspx。这允许多个读者和只有一个作者。