写点什么

分析一个错误使用 MemoryCache 导致的 BUG

作者:喵叔
  • 2021 年 11 月 22 日
  • 本文字数:858 字

    阅读完需:约 3 分钟

这个 Bug 是我在项目中发现的,原因是 MemoryCache 使用不当造成了一个不小的 Bug,虽说这个 Bug 很大部分人都知道,但是我觉得还是分享出来,记录一下。废话不多说,我们来看一下出错的代码(代码已经经过脱敏处理)


await using var services =   new ServiceCollection()    .AddMemoryCache()    .BuildServiceProvider();GetValidValues(5).Dump();GetValidValues(8).Dump();List<int> GetValidValues(int valueInt){    var memoryCache = services.GetRequiredService<IMemoryCache>();    var vs= memoryCache.GetOrCreate("t1", entry =>    {        return Enumerable.Range(1, 10).ToList();    });    vs.RemoveAll(x => x > valueInt);    return vs;}
复制代码


代码中 Dump 是扩展方法,它是把 list 内的元素输出出来,具体实现代码如下:


public static void Dump(this List<int> vs){    string v= string.Join("--", vs);    Console.WriteLine(v);}
复制代码


好了,来想一下上面的输出结果会是什么吧,期望的结果应该是每次都输出小于等于输入的值,实际是什么样的呢?实际输出结果如下:



从上图中第二次输出的结果是不是和你想的不一样呢,之所以出现上面问题是因为 MemoryCache 对象是直接保存在内存中的,缓存不变化时每次都返回同一个对象,如果发生了修改那么再次获取就是修改后的内容。因此正确做法是返回一个新对象而不是修改原来的对象,一个修改方法如下:


List<int> GetValidValues(int valueInt){    var memoryCache = services.GetRequiredService<IMemoryCache>();    var vs= memoryCache.GetOrCreate("t1", entry =>    {        return Enumerable.Range(1, 10).ToList();    });    return vs.Where(v => v <= valueInt).ToList();}
复制代码


修改后的输出结果如下:



总结:MemoryCache 背后其实就是 ConcurrentDictionary,value 其实是带着过期时间的 CacheEntry,因此在不过期并且没有发生变化的时候每次返回都是同一个缓存对象。作为缓存对象应进行只读操作,不应修改缓存对象,如需要修改应创建新对象而不是使用原来的对象。

发布于: 3 小时前阅读数: 5
用户头像

喵叔

关注

还未添加个人签名 2020.01.14 加入

还未添加个人简介

评论

发布
暂无评论
分析一个错误使用MemoryCache导致的BUG