[C#] 關於MemoryCache GetOrCreate 遇到 thread-safe 的問題

2023-02-04


同事 Roger 碰到一個小雷,跟我說過後,決定記錄一下

因為一些專案的緣故,開發環境是 ASP.NET Core  6 他使用的 

Microsoft.Extensions.Caching.Memory.MemoryCache 

但是在大量用戶存取(非同步) 的時候造成 Exception 

後來其實 Roger 也找到解法了也提供了我我測試一下後就記錄一下

測試,其實你發現在大量非同步執行的時候也就是用戶同步存取的時候會出現錯誤

這也可以很簡單的重現,理論上 GetOrCreate 不應該發生 Create 的動作


var memoryCacher = new Microsoft.Extensions.Caching.Memory.MemoryCache(new Microsoft.Extensions.Caching.Memory.MemoryCacheOptions()); int counter= 0; //10 Items. var str = "A,B,C,D,E,F,G,H,I,J"; Parallel.ForEach(str.Split(','), i => { var item = memoryCacher.GetOrCreate("TESTKEY", cacheEntry => { cacheEntry.SlidingExpiration = TimeSpan.FromMinutes(1); return Interlocked.Increment(ref counter); }); Console.Write(item +" "); }); Console.Write("Counter For MemoryCache :" + counter); //Result: //2 4 9 5 6 8 1 7 3 7 Counter For MemoryCache :9


之後改用一個套件 LazyCache 來解決這問題


LazyCache.IAppCache cacheService = new LazyCache.CachingService(); int counter2 = 0; Parallel.ForEach(str.Split(','), i => { var item = cacheService.GetOrAdd("test-key", cacheEntry => { cacheEntry.SlidingExpiration = TimeSpan.FromSeconds(10); return Interlocked.Increment(ref counter2); }); Console.Write(item + " "); }); Console.Write("Counter For CachingService :" + counter2); //Result: //1 1 1 1 1 1 1 1 1 1 Counter For CachingService :1


結果我就寫在註解了

感謝各位開源大大的幫忙


reference:

https://tpodolak.com/blog/2017/12/13/asp-net-core-memorycache-getorcreate-calls-factory-method-multiple-times/

https://learn.microsoft.com/zh-tw/dotnet/api/system.runtime.caching.cacheitempolicy.slidingexpiration?redirectedfrom=MSDN&view=dotnet-plat-ext-7.0

https://learn.microsoft.com/zh-tw/dotnet/api/microsoft.extensions.caching.memory.cacheextensions.getorcreate?view=dotnet-plat-ext-7.0

https://github.com/alastairtree/LazyCache

https://blog.novanet.no/asp-net-core-memory-cache-is-get-or-create-thread-safe/


當麻許的碎念筆記 2014 | Donma Hsu Design.