虽然这个问题与MemoryCache
类有关,但我可以想象使用Dictionary
或ConcurrentDictionary.GetOrAdd
同样需要valueFactory
- lambda也是一个冗长的操作。
本质上我想在每个项目的基础上同步/锁定线程。我知道MemoryCache
是线程安全的,但是,检查项目是否存在并在项目不存在时添加项目仍然需要同步。
请考虑以下示例代码:
public class MyCache
{
private static readonly MemoryCache cache = new MemoryCache(Guid.NewGuid().ToString());
public object Get(string id)
{
var cacheItem = cache.GetCachedItem(id);
if (cacheItem != null) return cacheItem.Value;
var item = this.CreateItem(id);
cache.Add(id, item, new CacheItemPolicy
{
SlidingExpiration = TimeSpan.FromMinutes(20)
});
return item;
}
private object CreateItem(string id)
{
// Lengthy operation, f.e. querying database or even external API
return whateverCreatedObject;
}
}
如您所见,我们需要同步cache.GetCachedItem
和cache.Add
。但由于CreateItem
是一个冗长的操作(因此MemoryCache
),我不想像这个代码那样锁定所有线程:
public object Get(string id)
{
lock (cache)
{
var item = cache.GetCachedItem(id);
if (item != null) return item.Value;
cache.Add(id, this.CreateItem(id), new CacheItemPolicy
{
SlidingExpiration = TimeSpan.FromMinutes(20)
});
}
}
此外,没有锁定不是一个选项,因为我们可以有多个线程为CreateItem
调用id
。
我可以做的是为每个Semaphore
创建一个唯一的名为id
,因此锁定按项目进行。但这将是一个系统资源杀手,因为我们不想在我们的系统上注册+ 100K命名信号量。
我确定我不是第一个需要这种同步的人,但我没有找到适合这种情况的任何问题/答案。
我的问题是,是否有人可以针对这个问题提出一种不同的,资源友好的方法?
更新
我发现this NamedReaderWriterLocker
类起初看起来很有前途,但使用起来很危险,因为当两个线程进入{{1时,两个线程可能会为同一个名称获取不同的ReaderWriterLockSlim
实例}} ConcurrentDictionary
同时。也许我可以在valueFactory
方法中使用一些额外的锁来使用这个实现。
答案 0 :(得分:2)
由于您的密钥是字符串,因此您可以锁定string.Intern(id)
。
MSDN文档:System.String.Intern
即
lock (string.Intern(id))
{
var item = cache.GetCachedItem(id);
if (item != null)
{
return item.Value;
}
cache.Add(id, this.CreateItem(id), new CacheItemPolicy
{
SlidingExpiration = TimeSpan.FromMinutes(20)
});
return /* some value, this line was absent in the original code. */;
}