Jam*_*gan 73 c# wcf multithreading memorycache
首先让我把它扔出去,我知道下面的代码不是线程安全的(更正:可能).我正在努力的是找到一个实现,并且我实际上可以在测试中失败.我现在正在重构一个大型WCF项目,它需要一些(大部分)静态数据缓存并从SQL数据库中填充.它需要每天至少过期和"刷新"一次,这就是我使用MemoryCache的原因.
我知道下面的代码不应该是线程安全的,但我不能让它在繁重的负载下失败,并且使谷歌搜索显示两种方式的实现变得复杂(有和没有锁结合辩论,无论它们是否必要.
在多线程环境中具有MemoryCache知识的人可以让我明确地知道我是否需要在适当的地方锁定,以便在检索/重新填充期间不会抛出删除调用(很少被调用但是它的要求).
public class MemoryCacheService : IMemoryCacheService
{
private const string PunctuationMapCacheKey = "punctuationMaps";
private static readonly ObjectCache Cache;
private readonly IAdoNet _adoNet;
static MemoryCacheService()
{
Cache = MemoryCache.Default;
}
public MemoryCacheService(IAdoNet adoNet)
{
_adoNet = adoNet;
}
public void ClearPunctuationMaps()
{
Cache.Remove(PunctuationMapCacheKey);
}
public IEnumerable GetPunctuationMaps()
{
if (Cache.Contains(PunctuationMapCacheKey))
{
return (IEnumerable) Cache.Get(PunctuationMapCacheKey);
}
var punctuationMaps = GetPunctuationMappings();
if (punctuationMaps == null)
{
throw new ApplicationException("Unable to retrieve punctuation mappings from the database.");
}
if (punctuationMaps.Cast<IPunctuationMapDto>().Any(p => p.UntaggedValue == null || p.TaggedValue == null))
{
throw new ApplicationException("Null values detected in Untagged or Tagged punctuation mappings.");
}
// Store data in the cache
var cacheItemPolicy = new CacheItemPolicy
{
AbsoluteExpiration = DateTime.Now.AddDays(1.0)
};
Cache.AddOrGetExisting(PunctuationMapCacheKey, punctuationMaps, cacheItemPolicy);
return punctuationMaps;
}
//Go oldschool ADO.NET to break the dependency on the entity framework and need to inject the database handler to populate cache
private IEnumerable GetPunctuationMappings()
{
var table = _adoNet.ExecuteSelectCommand("SELECT [id], [TaggedValue],[UntaggedValue] FROM [dbo].[PunctuationMapper]", CommandType.Text);
if (table != null && table.Rows.Count != 0)
{
return AutoMapper.Mapper.DynamicMap<IDataReader, IEnumerable<PunctuationMapDto>>(table.CreateDataReader());
}
return null;
}
}
Run Code Online (Sandbox Code Playgroud)
Han*_*ney 66
提供的默认MS MemoryCache完全是线程安全的.从派生的任何自定义实现MemoryCache可能不是线程安全的.如果您使用MemoryCache开箱即用,它是线程安全的.浏览我的开源分布式缓存解决方案的源代码,看看我是如何使用它的(MemCache.cs):
https://github.com/haneytron/dache/blob/master/Dache.CacheHost/Storage/MemCache.cs
ala*_*ree 34
虽然MemoryCache确实是线程安全的,因为其他答案已经指定,但它确实有一个常见的多线程问题 - 如果2个线程同时尝试Get从(或检查Contains)缓存,那么两者都将错过缓存,并且两者都将最终生成结果,然后两者都会将结果添加到缓存中.
通常这是不合需要的 - 第二个线程应该等待第一个线程完成并使用其结果而不是两次生成结果.
这是我写LazyCache的原因之一--LinCache是MemoryCache上的一个友好的包装器,可以解决这些问题.它也可以在Nuget上找到.
rob*_*pim 14
正如其他人所说,MemoryCache确实是线程安全的.然而,存储在其中的数据的线程安全性完全取决于您的使用.
引用Reed Copsey 的关于并发性和ConcurrentDictionary<TKey, TValue>类型的精彩文章.这当然适用于此.
如果两个线程同时调用此[GetOrAdd],则可以轻松构建两个TValue实例.
你可以想象,如果TValue建造起来很昂贵,这将特别糟糕.
为了解决这个问题,你可以Lazy<T>非常轻松地利用它,这恰好是非常便宜的构建.这样做可以确保我们进入多线程情况时,我们只构建多个实例Lazy<T>(这很便宜).
GetOrAdd()(GetOrCreate()在这种情况下MemoryCache)将返回相同的,单数Lazy<T>给所有线程,"额外"实例Lazy<T>被简单地扔掉.
由于在调用Lazy<T>之前不执行任何操作.Value,因此只构造对象的一个实例.
现在换一些代码!以下是IMemoryCache实现上述内容的扩展方法.它任意地SlidingExpiration基于int seconds方法参数进行设置.但这完全可以根据您的需求进行定制.
请注意,这是特定于.netcore2.0应用程序
public static T GetOrAdd<T>(this IMemoryCache cache, string key, int seconds, Func<T> factory)
{
return cache.GetOrCreate<T>(key, entry => new Lazy<T>(() =>
{
entry.SlidingExpiration = TimeSpan.FromSeconds(seconds);
return factory.Invoke();
}).Value);
}
Run Code Online (Sandbox Code Playgroud)
致电:
IMemoryCache cache;
var result = cache.GetOrAdd("someKey", 60, () => new object());
Run Code Online (Sandbox Code Playgroud)
为了全部异步执行此操作,我建议使用Stephen Toub在MSDN上的文章中的优秀AsyncLazy<T>实现.它结合了内置的懒惰初始化程序和承诺:Lazy<T>Task<T>
public class AsyncLazy<T> : Lazy<Task<T>>
{
public AsyncLazy(Func<T> valueFactory) :
base(() => Task.Factory.StartNew(valueFactory))
{ }
public AsyncLazy(Func<Task<T>> taskFactory) :
base(() => Task.Factory.StartNew(() => taskFactory()).Unwrap())
{ }
}
Run Code Online (Sandbox Code Playgroud)
现在的异步版本GetOrAdd():
public static Task<T> GetOrAddAsync<T>(this IMemoryCache cache, string key, int seconds, Func<Task<T>> taskFactory)
{
return cache.GetOrCreateAsync<T>(key, async entry => await new AsyncLazy<T>(async () =>
{
entry.SlidingExpiration = TimeSpan.FromSeconds(seconds);
return await taskFactory.Invoke();
}).Value);
}
Run Code Online (Sandbox Code Playgroud)
最后,致电:
IMemoryCache cache;
var result = await cache.GetOrAddAsync("someKey", 60, async () => new object());
Run Code Online (Sandbox Code Playgroud)
Eko*_*tin 11
看看这个链接:http://msdn.microsoft.com/en-us/library/system.runtime.caching.memorycache(v=vs.110).aspx
转到页面的最底部(或搜索文本"Thread Safety").
你会看见:
^线程安全
这种类型是线程安全的.
| 归档时间: |
|
| 查看次数: |
49213 次 |
| 最近记录: |