【发布时间】:2011-11-19 16:12:36
【问题描述】:
我有一个静态内存缓存,每小时仅写入一次(或更长),并且由许多线程以极高的速率读取。传统观点建议我遵循如下模式:
public static class MyCache
{
private static IDictionary<int, string> _cache;
private static ReaderWriterLockSlim _sharedLock;
static MyCache()
{
_cache = new Dictionary<int, string>();
_sharedLock = new ReaderWriterLockSlim();
}
public static string GetData(int key)
{
_sharedLock.EnterReadLock();
try
{
string returnValue;
_cache.TryGetValue(key, out returnValue);
return returnValue;
}
finally
{
_sharedLock.ExitReadLock();
}
}
public static void AddData(int key, string data)
{
_sharedLock.EnterWriteLock();
try
{
if (!_cache.ContainsKey(key))
_cache.Add(key, data);
}
finally
{
_sharedLock.ExitWriteLock();
}
}
}
作为一个微优化的练习,我如何才能在共享 read 锁的相对开销中减少更多滴答声? 编写的时间可能很昂贵,因为这种情况很少发生。我需要尽可能快地进行读取。在这种情况下,我可以只删除 read 锁(如下)并保持线程安全吗?或者有没有我可以使用的无锁版本?我熟悉内存防护,但不知道如何在这种情况下安全地应用它。
注意:我不拘泥于任何一种模式,所以只要最终结果更快并且在 C# 4.x.*中,任何建议都是受欢迎的。*
public static class MyCache2
{
private static IDictionary<int, string> _cache;
private static object _fullLock;
static MyCache2()
{
_cache = new Dictionary<int, string>();
_fullLock = new object();
}
public static string GetData(int key)
{
//Note: There is no locking here... Is that ok?
string returnValue;
_cache.TryGetValue(key, out returnValue);
return returnValue;
}
public static void AddData(int key, string data)
{
lock (_fullLock)
{
if (!_cache.ContainsKey(key))
_cache.Add(key, data);
}
}
}
【问题讨论】:
-
对于像这样的微优化,您确实必须首先进行分析。现在 ReaderLock 占用了多少 %?
-
您的第二个版本不是线程安全的。写作时没有什么可以保护阅读。
-
您准备在写入方面牺牲多少性能来换取读取性能?您可以制作一个非锁定版本,在需要添加数据时简单地替换字典。读取没有锁,但显然,在写入场景中要慢很多。
-
driis... 在完全锁定到位时,写入的完全锁定不会阻止读取吗?
-
@JoeGeeky,怎么可能?
GetData()中的代码对锁一无所知,因此它不会以任何方式对其作出反应。
标签: c# performance thread-safety micro-optimization