MemoryCache 使用不当导致的一个 BUG
MemoryCache 使用不當導致的一個 BUG
Intro
前幾天發(fā)現代碼里的一個 BUG,原因是 MemoryCache 使用不當,可以對于很多人來說可能都知道,但還是想分享記錄一下,避免以后寫出同樣的 BUG
Sample
直接來看下面的示例吧
await?using?var?services?=?new?ServiceCollection().AddMemoryCache().BuildServiceProvider();Console.WriteLine("-----?Bad?-----"); GetValidValues(5).Dump(); GetValidValues(8).Dump();List<int>?GetValidValues(int?threhold) {var?memoryCache?=?services.GetRequiredService<IMemoryCache>();var?values?=?memoryCache.GetOrCreate("test1",?entry?=>{return?Enumerable.Range(1,?10).ToList();});values.RemoveAll(x?=>?x?>?threhold);return?values; }上面的 Dump 是一個擴展方法就是把 list 內的元素輸出出來,實現如下:
public?static?void?Dump(this?List<int>?values) {var?value?=?string.Join(",",?values);Console.WriteLine(value); }好了,來想一下上面的輸出結果會是什么吧,期望的結果應該是每次都輸出小于等于輸入的值,實際是什么樣的呢?實際輸出結果如下:
Fix
可以看到第二次輸出的結果和我們的期望不同,之所以會出現上面的問題是因為 MemoryCache 的對象是直接保存在內存中的對象,緩存不發(fā)生變化時每次都是返回同一個對象,如果發(fā)生修改后面再獲取的就是修改后的狀態(tài)了,所以正確的做法應該要返回一個新的對象而不是修改原來的對象,一個修改方法如下:
List<int>?GetValidValues(int?threhold) {var?memoryCache?=?services.GetRequiredService<IMemoryCache>();var?values?=?memoryCache.GetOrCreate("test",?entry?=>{return?Enumerable.Range(1,?10).ToList();});return?values.Where(v?=>?v?<=?threhold).ToList(); }修改后的輸出結果如下:
More
MemoryCache 背后實際是一個 ConcurrentDictionary,value 是一個帶著過期時間的對象 CacheEntry,
在不過期,沒有發(fā)生變化的時候每次返回都是同一個對象,作為緩存對象,應該進行只讀操作,不應該修改緩存的對象,如果需要修改則應創(chuàng)建新的對象,而非使用原來的對象。
References
https://github.com/dotnet/runtime/blob/main/src/libraries/Microsoft.Extensions.Caching.Memory/src/MemoryCache.cs#L26
https://github.com/dotnet/runtime/blob/main/src/libraries/Microsoft.Extensions.Caching.Memory/src/CacheEntry.cs
https://github.com/WeihanLi/SamplesInPractice/blob/master/MemoryCacheSample/Program.cs
總結
以上是生活随笔為你收集整理的MemoryCache 使用不当导致的一个 BUG的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: 温故知新,.Net Core遇见WinF
 - 下一篇: 使用 Git Extensions 简单