确保单个创建缓存项目的缓存
我想要创建一个缓存,以便当一个项目不存在时,只有一个请求该项目的人花费时间来产生它,而另一些人同时请求它将被阻塞,直到结果被第一个人缓存为止。 以下是该场景的描述:
我想我会有一个ConcurrentDictionary中的缓存与一个DateTime只存储日期作为关键像ConcurrentDictionary<DateTime, CustomImmutableObject> _cache;
但是我看不到Thread 2和Thread 3能够等待什么。 即使存在另一个存储某种状态标志的ConcurrentDictionary,他们如何知道线程1何时完成?
有没有人有任何建议如何处理开发这样的缓存?
您可以使用ConcurrentDictionary<DateTime, Lazy<CustomImmutableObject>>
获取对象,如下所示:
myDictionary
.GetOrAdd(
someDateTime,
dt => new Lazy<CustomImmutableObject>(
() => CreateMyCustomImmutableObject()
)
).Value
Lazy<T>
在(默认的)线程安全模式下所做的保证将确保只发生一次初始化,并且实际被实例化之前的后续访问将被阻止。
在多线程场景中,访问线程安全的Lazy(Of T)对象的Value属性的第一个线程会将其初始化为所有线程上的所有后续访问,并且所有线程共享相同的数据。 因此,哪个线程初始化对象并且竞争条件是否良好并不重要。
详情请看这里。
下面是我写的一个测试,以确保我不会吐痰。 这应该说服你,一切都很好:
void Main()
{
var now=DateTime.UtcNow;
var d=new ConcurrentDictionary<DateTime, Lazy<CustomImmutableObject>>();
Action f=()=>{
var val=d
.GetOrAdd(
now,
dt => new Lazy<CustomImmutableObject>(
() => new CustomImmutableObject()
)
).Value;
Console.WriteLine(val);
};
for(int i=0;i<10;++i)
{
(new Thread(()=>f())).Start();
}
Thread.Sleep(15000);
Console.WriteLine("Finished");
}
class CustomImmutableObject
{
public CustomImmutableObject()
{
Console.WriteLine("CREATING");
Thread.Sleep(10000);
}
}
每次有人访问缓存对象时,您都可以对缓存对象进行lock
,如果缓存未命中,请对某个对象执行Monitor.Enter
以指示Monitor.Enter
对象创建,然后释放第一个锁。 创建完成后,对第二个锁对象使用Monitor.Exit
。
缓存访问通常锁定在主对象上,但创建锁定在第二个对象上。 如果您希望并行创建,则可以在字典中创建一个锁对象,其中的密钥与高速缓存的密钥相同。
我提出了以下似乎可行的方法,但是以锁定每次读取缓存为代价。 假设只有一个人可以一次为给定密钥添加cacheData数据是否安全?
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);
});
}
链接地址: http://www.djcxy.com/p/62995.html