MemoryCache在项目中用了很久,感觉比较简单,以前也看过里面的源代码,主要借用MemoryCacheStore来完成数据的存储,里面是线程安全的,MemoryCacheStore借用Hashtable来实现存储,如果已经有数据了,就把以前的删除然后在添加

我们来看看MemoryCache的实现:

public class MemoryCache : ObjectCache, IEnumerable, IDisposable {
private static readonly TimeSpan OneYear = new TimeSpan(, , , );
private static object s_initLock = new object();
private static MemoryCache s_defaultCache;
private static CacheEntryRemovedCallback s_sentinelRemovedCallback = new CacheEntryRemovedCallback(SentinelEntry.OnCacheEntryRemovedCallback);
private GCHandleRef<MemoryCacheStore>[] _storeRefs;
private int _storeCount;
private int _disposed;
private MemoryCacheStatistics _stats;
private string _name;
private PerfCounters _perfCounters;
private bool _configLess;
EventHandler _onAppDomainUnload;
UnhandledExceptionEventHandler _onUnhandledException; private MemoryCache() {
_name = "Default";
Init(null);
}
public MemoryCache(string name, NameValueCollection config = null) {
if (name == null) {
throw new ArgumentNullException("name");
}
if (name == String.Empty) {
throw new ArgumentException(R.Empty_string_invalid, "name");
}
if (String.Equals(name, "default", StringComparison.OrdinalIgnoreCase)) {
throw new ArgumentException(R.Default_is_reserved, "name");
}
_name = name;
Init(config);
}
private void Init(NameValueCollection config) {
_storeCount = Environment.ProcessorCount;
_storeRefs = new GCHandleRef<MemoryCacheStore>[_storeCount];
InitDisposableMembers(config);
}
public override object Get(string key, string regionName = null) {
return GetInternal(key, regionName);
}
private object GetInternal(string key, string regionName) {
if (regionName != null) {
throw new NotSupportedException(R.RegionName_not_supported);
}
if (key == null) {
throw new ArgumentNullException("key");
}
MemoryCacheEntry entry = GetEntry(key);
return (entry != null) ? entry.Value : null;
} internal MemoryCacheEntry GetEntry(String key) {
if (IsDisposed) {
return null;
}
MemoryCacheKey cacheKey = new MemoryCacheKey(key);
MemoryCacheStore store = GetStore(cacheKey);
return store.Get(cacheKey);
}
public override void Set(string key, object value, CacheItemPolicy policy, string regionName = null) {
if (regionName != null) {
throw new NotSupportedException(R.RegionName_not_supported);
}
if (key == null) {
throw new ArgumentNullException("key");
}
DateTimeOffset absExp = ObjectCache.InfiniteAbsoluteExpiration;
TimeSpan slidingExp = ObjectCache.NoSlidingExpiration;
CacheItemPriority priority = CacheItemPriority.Default;
Collection<ChangeMonitor> changeMonitors = null;
CacheEntryRemovedCallback removedCallback = null;
if (policy != null) {
ValidatePolicy(policy);
if (policy.UpdateCallback != null) {
Set(key, value, policy.ChangeMonitors, policy.AbsoluteExpiration, policy.SlidingExpiration, policy.UpdateCallback);
return;
}
absExp = policy.AbsoluteExpiration;
slidingExp = policy.SlidingExpiration;
priority = policy.Priority;
changeMonitors = policy.ChangeMonitors;
removedCallback = policy.RemovedCallback;
}
if (IsDisposed) {
if (changeMonitors != null) {
foreach (ChangeMonitor monitor in changeMonitors) {
if (monitor != null) {
monitor.Dispose();
}
}
}
return;
}
MemoryCacheKey cacheKey = new MemoryCacheKey(key);
MemoryCacheStore store = GetStore(cacheKey);
store.Set(cacheKey, new MemoryCacheEntry(key, value, absExp, slidingExp, priority, changeMonitors, removedCallback, this));
}
internal MemoryCacheStore GetStore(MemoryCacheKey cacheKey) {
// Dev10 865907: Math.Abs throws OverflowException for Int32.MinValue
int hashCode = cacheKey.Hash;
if (hashCode < ) {
hashCode = (hashCode == Int32.MinValue) ? : -hashCode;
}
int idx = hashCode % _storeCount;
return _storeRefs[idx].Target;
} private void InitDisposableMembers(NameValueCollection config) {
bool dispose = true;
try {
try {
_perfCounters = new PerfCounters(_name);
}
catch {
// ignore exceptions from perf counters
}
for (int i = ; i < _storeCount; i++) {
_storeRefs[i] = new GCHandleRef<MemoryCacheStore> (new MemoryCacheStore(this, _perfCounters));
}
_stats = new MemoryCacheStatistics(this, config);
AppDomain appDomain = Thread.GetDomain();
EventHandler onAppDomainUnload = new EventHandler(OnAppDomainUnload);
appDomain.DomainUnload += onAppDomainUnload;
_onAppDomainUnload = onAppDomainUnload;
UnhandledExceptionEventHandler onUnhandledException = new UnhandledExceptionEventHandler(OnUnhandledException);
appDomain.UnhandledException += onUnhandledException;
_onUnhandledException = onUnhandledException;
dispose = false;
}
finally {
if (dispose) {
Dispose();
}
}
} private void OnAppDomainUnload(Object unusedObject, EventArgs unusedEventArgs) {
Dispose();
} private void OnUnhandledException(Object sender, UnhandledExceptionEventArgs eventArgs) {
// if the CLR is terminating, dispose the cache.
// This will dispose the perf counters (see Dev10 680819).
if (eventArgs.IsTerminating) {
Dispose();
}
}
public void Dispose() {
if (Interlocked.Exchange(ref _disposed, ) == ) {
// unhook domain events
DisposeSafeCritical();
// stats must be disposed prior to disposing the stores.
if (_stats != null) {
_stats.Dispose();
}
if (_storeRefs != null) {
foreach (var storeRef in _storeRefs) {
if (storeRef != null) {
storeRef.Dispose();
}
}
}
if (_perfCounters != null) {
_perfCounters.Dispose();
}
GC.SuppressFinalize(this);
}
}
}

MemoryCacheStore的实现:

 internal sealed class MemoryCacheStore : IDisposable {
const int INSERT_BLOCK_WAIT = ;
const int MAX_COUNT = Int32.MaxValue / ;
private Hashtable _entries;
private Object _entriesLock;
private CacheExpires _expires;
private CacheUsage _usage;
private int _disposed;
private ManualResetEvent _insertBlock;
private volatile bool _useInsertBlock;
private MemoryCache _cache;
private PerfCounters _perfCounters; internal MemoryCacheStore(MemoryCache cache, PerfCounters perfCounters) {
_cache = cache;
_perfCounters = perfCounters;
_entries = new Hashtable(new MemoryCacheEqualityComparer());
_entriesLock = new Object();
_expires = new CacheExpires(this);
_usage = new CacheUsage(this);
InitDisposableMembers();
}
internal MemoryCacheEntry Get(MemoryCacheKey key) {
MemoryCacheEntry entry = _entries[key] as MemoryCacheEntry;
// has it expired?
if (entry != null && entry.UtcAbsExp <= DateTime.UtcNow) {
Remove(key, entry, CacheEntryRemovedReason.Expired);
entry = null;
}
// update outside of lock
UpdateExpAndUsage(entry);
return entry;
}
internal void Set(MemoryCacheKey key, MemoryCacheEntry entry) {
if (_useInsertBlock && entry.HasUsage()) {
WaitInsertBlock();
}
MemoryCacheEntry existingEntry = null;
bool added = false;
lock (_entriesLock) {
if (_disposed == ) {
existingEntry = _entries[key] as MemoryCacheEntry;
if (existingEntry != null) {
existingEntry.State = EntryState.RemovingFromCache;
}
entry.State = EntryState.AddingToCache;
added = true;
_entries[key] = entry;
}
} CacheEntryRemovedReason reason = CacheEntryRemovedReason.Removed;
if (existingEntry != null) {
if (existingEntry.UtcAbsExp <= DateTime.UtcNow) {
reason = CacheEntryRemovedReason.Expired;
}
RemoveFromCache(existingEntry, reason, delayRelease:true);
}
if (added) {
AddToCache(entry);
}
// Dev10 861163: Call Release after the new entry has been completely added so
// that the CacheItemRemovedCallback can take a dependency on the newly inserted item.
if (existingEntry != null) {
existingEntry.Release(_cache, reason);
}
}
private void AddToCache(MemoryCacheEntry entry) {
// add outside of lock
if (entry != null) {
if (entry.HasExpiration()) {
_expires.Add(entry);
} if (entry.HasUsage()
&& (!entry.HasExpiration() || entry.UtcAbsExp - DateTime.UtcNow >= CacheUsage.MIN_LIFETIME_FOR_USAGE)) {
_usage.Add(entry);
} entry.State = EntryState.AddedToCache;
entry.CallNotifyOnChanged();
if (_perfCounters != null) {
_perfCounters.Increment(PerfCounterName.Entries);
_perfCounters.Increment(PerfCounterName.Turnover);
}
}
}
internal void UpdateExpAndUsage(MemoryCacheEntry entry, bool updatePerfCounters = true) {
if (entry != null) {
if (entry.InUsage() || entry.SlidingExp > TimeSpan.Zero) {
DateTime utcNow = DateTime.UtcNow;
entry.UpdateSlidingExp(utcNow, _expires);
entry.UpdateUsage(utcNow, _usage);
} // DevDiv #67021: If this entry has an update sentinel, the sliding expiration is actually associated
// with that sentinel, not with this entry. We need to update the sentinel's sliding expiration to
// keep the sentinel from expiring, which in turn would force a removal of this entry from the cache.
entry.UpdateSlidingExpForUpdateSentinel(); if (updatePerfCounters && _perfCounters != null) {
_perfCounters.Increment(PerfCounterName.Hits);
_perfCounters.Increment(PerfCounterName.HitRatio);
_perfCounters.Increment(PerfCounterName.HitRatioBase);
}
}
else {
if (updatePerfCounters && _perfCounters != null) {
_perfCounters.Increment(PerfCounterName.Misses);
_perfCounters.Increment(PerfCounterName.HitRatioBase);
}
}
}
}

可见MemoryCache和MemoryCacheStore的实现都非常好理解。我们以web程序为例, 所有的数据都存在MemoryCacheStore的Hashtable中,但是不同的请求如何共享这个MemoryCacheStore数据了,一般我们采用static变量来实现,static变量跟着进程走,里面的线程共享它,我们来看看MemoryCache,它不是用静态变量,而是采用GCHandle来实现的,里面封装的GCHandleRef:

 internal class GCHandleRef<T> : IDisposable where T : class, IDisposable {
GCHandle _handle;
T _t; [SecuritySafeCritical]
[PermissionSet(SecurityAction.Assert, Unrestricted = true)]
public GCHandleRef(T t) {
_handle = GCHandle.Alloc(t);
} public T Target {
[SecuritySafeCritical]
[PermissionSet(SecurityAction.Assert, Unrestricted = true)]
get {
try {
T t = (T)_handle.Target;
if (t != null) {
return t;
}
}
catch (InvalidOperationException) {
// use the normal reference instead of throwing an exception when _handle is already freed
}
return _t;
}
} [SecuritySafeCritical]
[PermissionSet(SecurityAction.Assert, Unrestricted = true)]
public void Dispose() {
Target.Dispose();
// Safe to call Dispose more than once but not thread-safe
if (_handle.IsAllocated) {
// We must free the GC handle to avoid leaks.
// However after _handle is freed we no longer have access to its Target
// which will cause AVs and various race conditions under stress.
// We revert to using normal references after disposing the GC handle
_t = (T)_handle.Target;
_handle.Free();
}
}
}

在MemoryCache的MemoryCache构造函数里面会调用Init方法,里面会初始化_storeRefs数组(  _storeRefs = new GCHandleRef<MemoryCacheStore>[_storeCount];),最主要的是还调用InitDisposableMembers方法,在InitDisposableMembers方法里面给每个_storeRefs初始化一个实例,这些实例不会赔GC自动回收,而是在AppDomain的DomainUnload和UnhandledException事件里回收,回收也采用了原子锁。

我们在使用c#托管代码时,内存地址和GC回收不是我们关心的,CLR已经给我们进行了暗箱操作。但是有时候我们想使用类似C语言那种方式直接对内存进行操作,或者涉及到非托管代码的调用,此时就需要保护内存地址,防止GC垃圾回收机制将内存收回。因为一旦内存被CLR回收掉,直接造成非托管代码再次访问这块内存时失效,导致程序崩溃。

C#中直接操作内存主要有以下三种方法:
1、GCHandle类用于提供用于从非托管内存访问托管对象的方法。下面通过程序进行介绍:

//托管的内存区域
Int16[] Mangement_Mem = new Int16[4]{ 4, 3, 2, 1 };
GCHandle gch = GCHandle.Alloc(Mangement_Mem,GCHandleType.Normal);
/*
为托管内存Mangement_Mem分配GCHandle句柄,它保护Mangement_Mem对象不被垃圾回收。但是此时Mangement_Mem在内存中的地址可能会改变,不管内存如何改变,其对象的的句柄的整数表示即gch值是不变的,因此可以将其值传给非托管函数中去使用。当不再需要 GCHandle时,必须通过Free将其释放,此后GC垃圾处理器可能才会对其回收。
*/
/*
GCHandle.Alloc(Mangement_Mem,GCHandleType.Normal)作用类似如下:
GC.KeepAlive(Mangement_Mem);
从Mangement_Mem句柄表现形式再次转化为句GCHandle对象
IntPtr Ptr_Mem = GCHandle.ToIntPtr(gch);
GCHandle handle = GCHandle.FromIntPtr(Ptr_Mem);
*/
//获取该GCHandle对象表示的实际对象。
Int16[] array = (Int16[]) handle.Target;

GCHandle.Alloc(Mangement_Mem,GCHandleType.Normal);GCHandle.Alloc函数的第二个形参,除了有GCHandleType.Normal 外,还有Pinned。但Normal不会固定其地址,只是保证内存不被GC回收。而Pinned可以将地址固定住,Pinned后这将防止垃圾回收器移动内存地址。

2、 Marshal
C#中提供了一个方法集,这些方法用于分配非托管内存、复制非托管内存块、将托管类型转换为非托管类型,此外还提供了在与非托管代码交互时使用的其他杂项方法。也只有c++.net才有托管,非托管的概念,纯的C++没有这个概念。java可以认为所有东西都是托管的。这就是通过marshal类实现。

Marshal可以实现结构体和字节序之间的转化。具体可以搜索一下网上的资料。

3、通过fixe固定地址。将我们申请的资源通过关键字进行固定,达到使CLR不使用垃圾回收机制操作我们保护的内存。

class StudentInfo
{
public string Name { set; get; }
}
class Program
{
private const int OptionsMask = 0xFFFF;
static void Main(string[] args)
{
var a = new StudentInfo { Name = "Gavin" };
var b = ObjectToByte(a);
var c = ByteToObject(ref a, b);
Console.ReadKey();
}
/// <summary>
/// 将结构体转换成字节数组
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public static byte[] ObjectToByte<T>(T obj)
{
//得到结构体的大小
int size = Marshal.SizeOf<T>(obj);
//创建byte数组
byte[] bytes = new byte[size];
//分配结构体大小的内存空间
IntPtr structPtr = Marshal.AllocHGlobal(size);
//将结构体拷到分配好的内存空间
Marshal.StructureToPtr(obj, structPtr, false);
//从内存空间拷到byte数组
Marshal.Copy(structPtr, bytes, , size);
//释放内存空间
Marshal.FreeHGlobal(structPtr);
//返回byte数组
return bytes;
} /// <summary>
/// 将字节数组转换成结构体
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="_struct"></param>
/// <param name="buffer"></param>
/// <returns></returns>
public static int ByteToObject<T>(ref T _struct, byte[] buffer)
{
try
{
if ((buffer != null) && (buffer.Length > ))
{
GCHandle pinned = GCHandle.Alloc(buffer, GCHandleType.Pinned);
try
{
_struct = (T)Marshal.PtrToStructure(pinned.AddrOfPinnedObject(), typeof(T));
return buffer.Length;
}
finally
{
pinned.Free();
}
}
else
return ;
}
catch
{
return -;
}
} }

C# MemoryCache GCHandle的更多相关文章

  1. 【无私分享:ASP.NET CORE 项目实战(第十一章)】Asp.net Core 缓存 MemoryCache 和 Redis

    目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 经过 N 久反复的尝试,翻阅了网上无数的资料,GitHub上下载了十几个源码参考, Memory 和 Redis 终于写出一个 ...

  2. 基于MemoryCache的缓存辅助类

    背景: 1. 什么是MemoryCache? memoryCache就是用电脑内存做缓存处理 2.使用范围? 可用于不常变的数据,进行保存在内存中,提高处理效率 代码: /// <summary ...

  3. 从零开始,搭建博客系统MVC5+EF6搭建框架(3),添加Nlog日志、缓存机制(MemoryCache、RedisCache)、创建控制器父类BaseController

    一.回顾系统进度以及本章概要 目前博客系统已经数据库创建.以及依赖注入Autofac集成,接下来就是日志和缓存集成,这里日志用的是Nlog,其实还有其他的日志框架如log4,这些博客园都有很多介绍,这 ...

  4. Android开源框架:Universal-Image-Loader解析(二)MemoryCache

  5. 《你不常用的c#之二》:略谈GCHandle

    我们在使用c#托管代码时,内存地址和GC回收那不是我们关心的,CLR已经给我们暗箱操作.但是如果我们在c#中调用了一个非托管代码,比如vc的DLL,而且他有个回调函数,需要引用c#中的某个对象并操作, ...

  6. .NET 4.0 MemoryCache with SqlChangeMonitor

    Summary There isn't a lot of documentation on the internet about how to use the SqlChangeMonitor wit ...

  7. 缓存管理Memorycache 的使用

      前言:什么是memoryCache? 一种缓存管理技术,某些只读数据频繁操作数据库,会对系统的性能有很大的开销,所以我们使用缓存技术,当数据库内容更新,我们在更更新缓存的数据值.目前缓存讲技术的产 ...

  8. Asp.net Core 缓存 MemoryCache 和 Redis

    Asp.net Core 缓存 MemoryCache 和 Redis 目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 经过 N 久反复的尝试,翻阅了网上无数的资料,GitH ...

  9. 在.NET项目中使用PostSharp,使用MemoryCache实现缓存的处理(转)

    在之前一篇随笔<在.NET项目中使用PostSharp,实现AOP面向切面编程处理>介绍了PostSharp框架的使用,试用PostSharp能给我带来很多便利和优势,减少代码冗余,提高可 ...

随机推荐

  1. FastJson 数组、List、Set、Map基本序列化与日期格式化

    摘要: FastJson可以直接序列化数组.List.Set.Map等,也可以直接支持日期类型数据的格式化,包括java.util.Date.java.sql.Date.java.sql.Timest ...

  2. Codeforces 342D Xenia and Dominoes 状压dp

    码就完事了. #include<bits/stdc++.h> #define LL long long #define fi first #define se second #define ...

  3. jquery.zclip.js粘贴功能

    jquery的粘贴插件: 如下是代码: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> ...

  4. Hadoop| MapReduce01 概述

    概述 分布式运算程序: 优点:易于编程:良好扩展性:高容错性:适合PB级以上海量数据的离线处理: 缺点:不擅长实时计算:不擅长流式计算:不擅长DAG有向图计算: 核心思想: 1)分布式的运算程序往往需 ...

  5. Centos7使用yum命令安装Mysql5.6.X

    首先:具体的安装步骤在mysql官方文档上都有详细的描述. 文档虽然是英文,不过很容易理解,我就不一一翻译了. 官方文档地址:https://dev.mysql.com/doc/refman/5.6/ ...

  6. js面向对象实例

    JSON方式来编写对象简单 不适合多个对象 var json={a:12, show:function (){ alert(this); }};json.show(); //理所当然弹出的是objec ...

  7. Get与Post方法的区别

    Http协议定义了很多与服务器交互的方法,最基本的有4种,分别是GET,POST,PUT,DELETE. 一个URL地址用于描述一个网络上的资源,而HTTP中的GET, POST, PUT, DELE ...

  8. linux学习笔记 yum 在线管理软件包

    -y  如果yum在工作过程中需要使用者响应.这个参数可以直接回答yes #yum list 列出资源库中所有可安装或者可更新的rpm包 #yum perl 列出为perl的包 #yum perl* ...

  9. 6. 合并排序数组 II

    6. Merge Two Sorted Arrays Description Merge two given sorted integer array A and B into a new sorte ...

  10. 前缀和的应用 CodeForces - 932B Recursive Queries

    题目链接: https://vjudge.net/problem/1377985/origin 题目大意就是要你把一个数字拆开,然后相乘. 要求得数要小于9,否则递归下去. 这里用到一个递归函数: i ...