Dot-Net

.Net Core MemoryCache PostEvictionCallback 無法正常工作

  • December 23, 2017

我在 Microsoft.Extensions.Caching.Memory.MemoryCache 中設置了具有滑動到期的記憶體項。我想在每次記憶體項過期時觸發回調,但直到我查詢記憶體中過期的記憶體項時才會觸發回調。

這是程式碼:

using System;
using Microsoft.Extensions.Caching.Memory;

namespace Memcache
{
   public class Program
   {
       private static MemoryCache _cache;
       private static int _cacheExpSecs;

       public static void Main(string[] args)
       {
           _cache = new MemoryCache(new MemoryCacheOptions());
           _cacheExpSecs = 2;

           var cacheEntryOptions = new MemoryCacheEntryOptions()
           .SetSlidingExpiration(TimeSpan.FromSeconds(_cacheExpSecs))
           .RegisterPostEvictionCallback(callback: EvictionCallback);

           _cache.Set(1, "One", cacheEntryOptions);
           _cache.Set(2, "Two", cacheEntryOptions);

           var autoEvent = new System.Threading.AutoResetEvent(false);

           System.Threading.Timer timer = new System.Threading.Timer(checkCache, autoEvent, 1000, 6000);

           Console.Read();
       }

       private static void checkCache(Object o)
       {
           if(_cache.Get(1)!=null)
           {
               Console.WriteLine(string.Format(@"checkCache: Cache with key {0} will be removed manually and will trigger the callback.", 1));
               _cache.Remove(1);
           }
           else
           {
               Console.WriteLine(string.Format("checkCache: Cache with key {0} is expired.", 1));
           }


           if(_cache.Get(2) != null)
           {
               Console.WriteLine(string.Format("checkCache: Cache with key {0} will expire in {1} seconds, but won't trigger the callback until we check it's value again.", 2, _cacheExpSecs));
           }
           else
           {
               Console.WriteLine(string.Format("checkCache: Cache with key {0} is expired.", 2));
           }

       }

       private static void EvictionCallback(object key, object value, EvictionReason reason, object state)
       {
           Console.WriteLine();
           Console.WriteLine("/*****************************************************/");
           Console.WriteLine(string.Format("/*  EvictionCallback: Cache with key {0} has expired.  */", key));
           Console.WriteLine("/*****************************************************/");
           Console.WriteLine();
       }
   }
}

發生這種情況是因為在您查詢該項目並檢查到期之前,該項目不會被驅逐

(來自 來源MemoryCacheStore.Get(MemoryCacheKey key)

   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;
   }

或者Trim()由於記憶體壓力而在內部呼叫時

(來自 來源TrimInternal(int percent)

/*SNIP*/
       trimmedOrExpired = _expires.FlushExpiredItems(true);
       if (trimmedOrExpired < toTrim) {
           trimmed = _usage.FlushUnderUsedItems(toTrim - trimmedOrExpired);
           trimmedOrExpired += trimmed;
       }
/*SNIP*/

如果您的系統目前記憶體不足,無法觸發修剪,那麼項目將被驅逐的唯一時間是在嘗試檢索它們時。

要添加到接受答案和評論,您可以使用過期取消令牌強制記憶體過期並自動驅逐。

int expirationMinutes = 60;
var expirationTime = DateTime.Now.Add(expirationMinutes);
var expirationToken = new CancellationChangeToken(
   new CancellationTokenSource(TimeSpan.FromMinutes(expirationMinutes + .01)).Token);

var cacheEntryOptions = new MemoryCacheEntryOptions()
        // Pin to cache.
        .SetPriority(CacheItemPriority.NeverRemove)
        // Set the actual expiration time
        .SetAbsoluteExpiration(expirationTime)
        // Force eviction to run
        .AddExpirationToken(expirationToken)
        // Add eviction callback
        .RegisterPostEvictionCallback(callback: CacheItemRemoved, state: this); 

`

缺乏內置的計時器行為,舊的曾經有的,應該是設計使然,這是推薦的。見:https ://github.com/aspnet/Caching/issues/248

引用自:https://stackoverflow.com/questions/42535408