IMemoryCache、MemoryCache、键空间、依赖注入和 Clear()

问题描述 投票:0回答:1

我遇到了一个看似简单的问题,但最终却失败了。我们有:

public class MyService
    private readonly IMemoryCache cache;
    private ConcurrentDictionary<int, bool> cacheKeys;
    public MyController(IMemoryCache m) { cache = m; }

    public UserModel GetUser(int userId) {
       // Gets the user from the cache if possible, adds it if not
       if (cache.TryGetValue(userId, out var result)) return result;
       result = new UserModel(userId);
       cache.Set(userId, result); // Absolute and Sliding expiration omitted for brevity
       cacheKeys.Add(userId, true);
       return result;
    }

    public void InvalidateCache() {
        // Existing comment reads something along the lines of
        // Since IMemoryCache has no clear, we do this
        foreach (var entry in cacheKeys.ToList())
            cache.Remove(entry.Key);
    }

    // RegisterCacheEvictionHandler omitted for brevity
}

该服务由

AddSingleton()
添加到依赖注入,并且
IMemoryCache
中没有引用
MemoryCache
Startup.cs

所以问题是多方面的。

  1. 添加到
    IMemoryCache
    和添加到
    ConcurrentDictionary
    可以分开,导致字典与缓存过时。这可能会导致按键在
    ConcurrentDictionary
    中堆积。
  2. 调用
    MemoryCache.Clear()
    会破坏
    IMemoryCache
    的其他用户。其他用户之一在缓存中有不可驱逐的对象。
  3. 钥匙空间共享;此代码是所有其他用户使用
    Guid
    键意外运行的。
  4. 现有代码设置了生存时间;但没有任何地方设置缓存大小或压缩或类似的东西。
  5. 我们的服务器上有多个重量级进程。这已经迫使我们切换到工作站GC。不确定这对
    MemoryCache
    意味着什么。

(MemoryCache 是 ASP.NET Core 版本,不是向后兼容版本)

c# asp.net-core memorycache
1个回答
0
投票

使 MemoryCache 中的一组已知对象失效的能力几乎是内置的;只是不是以一种容易被发现的方式。

如果我们这样做:

   private readonly IMemoryCache cache;
   private Tuple<MemoryCacheEntryOptions, CancellationTokenSource> options;

   public UserService(MemoryCache m) {
        cache = m;
        options = CreateOptions();
   }

   private Tuple<MemoryCacheEntryOptions, CancellationTokenSource> CreateOptions() {
        var source = new CancellationTokenSource();
        var options = new MemoryCacheEntryOptions()
              .SetSlidingExpiration(new TimeSpan(...))
              .SetAbsoluteExpiration(new TimeSpan(...))
              // This is the heart of the solution. We can signal immediate expiration of a set of objects with one call.
              .AddExpirationToken(new CancellationChangeToken(source.Token));
        return Tuple.Create<options, source>();
    }

    public UserModel GetUser(int userId) {
       // Gets the user from the cache if possible, adds it if not
       if (cache.TryGetValue(userId, out var result)) return result;
       var cacheoptions = options.Item1;
       result = new UserModel(userId);
       cache.Set(userId, result, cacheoptions);
       return result;
    }

    public void InvalidateCache() {
        var newOptions = CreateOptions();
        Interlocked.Exchange(ref options, newOptions);
        newOptions.Item2.Cancel();
        newOptions.Item2.Dispose();
    }

然后

InvalidateCache()
无需锁定即可工作。由于没有第二个密钥集合,因此不会失去同步。这与其自身的缓存大小限制正确交互;只能重新访问 .Set() 调用。

© www.soinside.com 2019 - 2024. All rights reserved.