我正在使用 3.5 .NET Framework 进行开发,我需要在多线程场景中使用缓存,并为其项目提供延迟加载模式。在阅读了网络上的几篇文章后,我尝试编写自己的实现。
public class CacheItem
{
public void ExpensiveLoad()
{
// some expensive code
}
}
public class Cache
{
static object SynchObj = new object();
static Dictionary<string, CacheItem> Cache = new Dictionary<string, CacheItem>();
static volatile List<string> CacheKeys = new List<string>();
public CacheItem Get(string key)
{
List<string> keys = CacheKeys;
if (!keys.Contains(key))
{
lock (SynchObj)
{
keys = CacheKeys;
if (!keys.Contains(key))
{
CacheItem item = new CacheItem();
item.ExpensiveLoad();
Cache.Add(key, item);
List<string> newKeys = new List<string>(CacheKeys);
newKeys.Add(key);
CacheKeys = newKeys;
}
}
}
return Cache[key];
}
}
如您所见,Cache 对象既使用存储真实键值对的字典,也使用仅复制键的列表。当线程调用 Get 方法时,它会读取静态共享密钥列表(声明为 volatile)并调用 Contains 方法以查看密钥是否已经存在,如果不存在,则在开始延迟加载之前使用双重检查锁定模式。在加载结束时,会创建一个新的密钥列表实例并将其存储在静态变量中。
显然,我处于重新创建整个键列表的成本与单个项目加载的成本几乎无关的情况。
我希望有人能告诉我它是否真的是线程安全的。当我说“线程安全”时,我的意思是每个读取器线程都可以避免损坏或脏读取,并且每个写入器线程只加载丢失的项目一次。