C# 保证一次创建缓存项的缓存
我想创建一个缓存,这样当一个项目不存在时,只有一个请求该项目的人会花时间生成它,而其他同时请求该项目的人只会阻塞,直到第一个人缓存结果。以下是该场景的描述:C# 保证一次创建缓存项的缓存,c#,.net,caching,concurrency,C#,.net,Caching,Concurrency,我想创建一个缓存,这样当一个项目不存在时,只有一个请求该项目的人会花时间生成它,而其他同时请求该项目的人只会阻塞,直到第一个人缓存结果。以下是该场景的描述: 线程1进入并请求DateA的缓存数据 线程1发现它不在缓存中,并启动相对较长的 生成信息的过程 线程2进入一个窗口,看到当前正在生成信息,等待某种类型的锁 线程3也进入并看到当前正在生成信息,等待某种类型的锁 线程4进入并请求已在缓存中且根本不等待的不同密钥的数据 线程1完成生成并使用值更新缓存 线程2和线程3都会唤醒并获得现在缓存中的结
- 线程1进入并请求DateA的缓存数据
- 线程1发现它不在缓存中,并启动相对较长的 生成信息的过程
- 线程2进入一个窗口,看到当前正在生成信息,等待某种类型的锁
- 线程3也进入并看到当前正在生成信息,等待某种类型的锁
- 线程4进入并请求已在缓存中且根本不等待的不同密钥的数据
- 线程1完成生成并使用值更新缓存
- 线程2和线程3都会唤醒并获得现在缓存中的结果
ConcurrentDictionary\u缓存代码>
但是我看不出线程2和线程3如何能够等待一些东西。即使有另一个ConcurrentDictionary存储了某种状态标志,它们如何知道线程1何时完成
有人对如何开发这样的缓存有什么建议吗?每次有人访问缓存对象时,都可以在缓存对象上设置一个锁,如果缓存未命中,则执行监视。在某个对象上输入以指示正在创建对象,然后释放第一个锁。创建完成后,在第二个锁对象上使用监视器。退出
缓存访问通常锁定在主对象上,但创建锁定在第二个对象上。如果希望创建并行进行,可以在字典中创建一个锁对象,其中的键与缓存的键相同。每次有人访问缓存对象时,可以在缓存对象上放置一个锁
,如果缓存未命中,则执行监视。在某个对象上输入,以指示正在创建对象,然后释放第一个锁。创建完成后,在第二个锁对象上使用监视器。退出
缓存访问通常锁定在主对象上,但创建锁定在第二个对象上。如果希望创建并行进行,可以在字典中创建一个锁对象,其中键与缓存的键相同。可以使用ConcurrentDictionary
获取对象,如下所示:
myDictionary
.GetOrAdd(
someDateTime,
dt => new Lazy<CustomImmutableObject>(
() => CreateMyCustomImmutableObject()
)
).Value
myDictionary
格托拉德先生(
有时,
dt=>new Lazy(
()=>CreateMyCustomImmutableObject()
)
).价值
在(默认)线程安全模式下,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);
}
}
void Main()
{
var now=DateTime.UtcNow;
var d=新的ConcurrentDictionary();
动作f=()=>{
var=d
格托拉德先生(
现在,
dt=>new Lazy(
()=>新的CustomImmutableObject()
)
).价值;
控制台写入线(val);
};
对于(int i=0;if()).Start();
}
睡眠(15000);
控制台。写入线(“完成”);
}
类CustomImmutableObject
{
公共CustomImmutableObject()
{
Console.WriteLine(“创建”);
睡眠(10000);
}
}
您可以使用ConcurrentDictionary
获取对象,如下所示:
myDictionary
.GetOrAdd(
someDateTime,
dt => new Lazy<CustomImmutableObject>(
() => CreateMyCustomImmutableObject()
)
).Value
myDictionary
格托拉德先生(
有时,
dt=>new Lazy(
()=>CreateMyCustomImmutableObject()
)
).价值
在(默认)线程安全模式下,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);
}
}
void Main()
{
var now=DateTime.UtcNow;
var d=新的ConcurrentDictionary();
动作f=()=>{
var=d
格托拉德先生(
现在,
dt=>new Lazy(
()=>新的CustomImmutableObject()
)
).价值;
控制台写入线(val);
};
对于(int i=0;if()).Start();
}
睡眠(15000);
控制台。写入线(“完成”);
}
类CustomImmutableObject
{
公共CustomImmutableObject()
{
Console.WriteLine(“创建”);
睡眠(10000);
}
}
我提出了下面的方法,看起来很有效,但代价是每次读取缓存时都要锁定。假设只有一个人可以添加到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);
});
}
静态ConcurrentDictionary缓存访问=新建ConcurrentDictionary();
静态ConcurrentDictionary cacheData=新ConcurrentDictionary();
静态int GetValue(日期时间键)
{