我有一个静态字典,我想用它作为ASP.NET应用程序的内部缓存。读取次数将大大超过写入次数,并且我希望确保以线程安全的方式执行此操作,而不会因无关锁而不必要地损害性能。
我有两个实现 - 第一个使用简单的锁对象和lock
键工作,第二个使用ReadWriteLockSlim
。
public class ConcurrentCache
{
private static readonly object LocationsLock = new object();
private static bool _locationsLoaded = false;
private static readonly ConcurrentDictionary<long, Location> Locations =
new ConcurrentDictionary<long, Location>();
public Location Get(long id)
{
EnsureLocationsDictionaryIsPopulated();
return Locations[id];
}
private void EnsureLocationsDictionaryIsPopulated()
{
if (Locations.Count > 0 || _locationsLoaded) return;
// Still locking, even though I'm using a ConcurrentDictionary,
// so that all locations are loaded only once and I don't have
// to worry about locking the reads.
lock (LocationsLock)
{
if (Locations.Count > 0 || _locationsLoaded) return;
PopulateLocationsDictionary();
_locationsLoaded = true;
}
}
// see below for other methods
}
public class ReadWriteCache
{
private static readonly ReaderWriterLockSlim LockSlim =
new ReaderWriterLockSlim();
private static bool _locationsLoaded = false;
private static readonly Dictionary<long, Location> Locations =
new Dictionary<long, Location>();
public Location Get(long id)
{
EnsureLocationsDictionaryIsPopulated();
return Locations[id];
}
private void EnsureLocationsDictionaryIsPopulated()
{
if (Locations.Count > 0 || _locationsLoaded) return;
LockSlim.EnterWriteLock();
try
{
if (Locations.Count > 0 || _locationsLoaded) return;
PopulateLocationsDictionary();
_locationsLoaded = true;
}
finally
{
LockSlim.ExitWriteLock();
}
}
// see below for other methods
}
两个类都有相同的两种方法:
private void PopulateLocationsDictionary()
{
var items = LoadAllLocationsFromExternalSource();
if (items == null || items.Count == 0) return;
for (int i = 0; i < items.Count; i++)
{
var location = items[i];
Locations[location.Id] = location;
}
}
/// <summary>
/// This method actually calls an external API and takes
/// at least 5 seconds to run.
/// </summary>
/// <returns></returns>
private List<Location> LoadAllLocationsFromExternalSource()
{
return new List<Location>
{
new Location
{Id = 5, Value1 = "one", Value2 = "two", Value3 = "three"},
new Location
{Id = 10, Value1 = "I", Value2 = "II", Value3 = "III"},
new Location
{Id = 42, Value1 = "un", Value2 = "deux", Value3 = "trois"}
};
}
我从这篇文章(When is ReaderWriterLockSlim better than a simple lock?)中看到,当访问模式主要涉及读取时,ReadWriteLockSlim
预计会胜过标准锁定。在我的两个场景中仍然如此吗? ReadWriteLockSlim
与ConcurrentDictionary
相比如何?我还没有考虑其他因素吗?
答案 0 :(得分:1)
我建议使用最简单的方法(在这种情况下只是一个ConcurrentDictionary
,没有额外的锁)。 ConcurrentDictionary
课程的设计完全符合您的要求。
然后我建议通过IDictionary
界面将缓存暴露给外界。
如果将来在这个领域出现性能问题(不太可能,瓶颈往往不在您预期的位置),您只需要更改这一段代码,其余的就是申请不会受到影响。
您可能会陷入过早优化的陷阱,这是一个很大的生产力和可维护性杀手。
如果确实想知道哪个更快,请设置测试应用程序,配置文件 2个具有不同负载的情况。对于您的特定情况,您将获得比我们能够在Stack Overflow上提供的更准确的答案!