我想出了下面的办法,似乎工作,但在每一个锁的成本读取缓存的出来。假设只有一个人可以一次添加一个给定密钥的cacheData 是否安全?只要你重新检查任何对象,你正在寻找的锁内,你应该没问题缓存
public static object GetItem(DateTime dt)
{
// Check to see if the cache already has my item
if (_dictionary.ContainsKey(dt))
return _dictionary[dt];
lock (_lock)
{
// Check the cache AGAIN in case a previous thread inserted our value
if (_dictionary.ContainsKey(dt))
return _dictionary[dt];
// Add the generate object to the dictionary
_dictionary.Add(dt, GenerateMyObject(dt));
}
}
private static Dictionary<DateTime, object> _dictionary = new Dictionary<DateTime, object>();
private static object _lock = new object();
:
static ConcurrentDictionary<DateTime, object> cacheAccess = new ConcurrentDictionary<DateTime, object>();
static ConcurrentDictionary<DateTime, int> cacheData = new ConcurrentDictionary<DateTime, int>();
static int GetValue(DateTime key)
{
var accessLock = cacheAccess.GetOrAdd(key, x => new object());
lock (accessLock)
{
int resultValue;
if (!cacheData.TryGetValue(key, out resultValue))
{
Console.WriteLine("Generating {0}", key);
Thread.Sleep(5000);
resultValue = (int)DateTime.Now.Ticks;
if (!cacheData.TryAdd(key, resultValue))
{
throw new InvalidOperationException("How can something else have added inside this lock?");
}
}
return resultValue;
}
}
static void Main(string[] args)
{
var keys = new[]{ DateTime.Now.Date, DateTime.Now.Date.AddDays(-1), DateTime.Now.Date.AddDays(1), DateTime.Now.Date.AddDays(2)};
var rand = new Random();
Parallel.For(0, 1000, (index) =>
{
var key = keys[rand.Next(keys.Length)];
var value = GetValue(key);
Console.WriteLine("Got {0} for key {1}", value, key);
});
}
谢谢!这看起来是一个好的解决方案。在relfector中查看懒惰它基本上是一个Tuple
确实。就个人而言,我希望看到应用于ConcurrentDictionary的GetOrAdd方法的相同方法,但在并发使用中,可以多次调用添加委托,并且除了一种情况外,都会抛弃返回值。当然,使用懒惰可以缓解这一点。 – spender
“我希望看到应用于ConcurrentDictionary的GetOrAdd方法的相同方法” - 此方法将允许在添加委托中调用的任意外部代码在锁定期间运行 - 这可能会导致难以调试错误(重入;长时间锁定)。恕我直言,目前的设计是最好的折衷。 – Joe