如何实现数据大小限制的缓存?

How to implement Caching with data size limit?

我有多个线程请求必须通过网络加载的数据。 为了减少网络流量和加快响应速度,我想缓存经常被请求的数据。我也想限制缓存的数据大小。

我的 class 看起来像这样:

public class DataProvider
{
    private ConcurrentDictionary<string, byte[]> dataCache;
    private int dataCacheSize;
    private int maxDataCacheSize;
    private object dataCacheSizeLockObj = new object();

    public DataProvider(int maxCacheSize)
    {
        maxDataCacheSize = maxCacheSize;
        dataCache = new ConcurrentDictionary<string,byte[]>();
    }

    public byte[] GetData(string key)
    {
        byte[] retVal;

        if (dataCache.ContainsKey(key))
        {
            retVal = dataCache[key];
        }
        else
        {
            retVal = ... // get data from somewhere else

            if (dataCacheSize + retVal.Length <= maxDataCacheSize)
            {
                lock (dataCacheSizeLockObj)
                {
                    dataCacheSize += retVal.Length;
                }
                dataCache[key] = retVal;
            }
        }
        return retVal;
    }
}

我的问题是:如何确保 dataCacheSize 始终具有正确的值?如果两个线程同时请求相同的未缓存数据,它们都会将自己的数据写入缓存,这没有问题,因为数据相同,第二个线程只会用相同的数据覆盖缓存数据。但是我怎么知道它是否被覆盖以避免计算它的大小两次?

也可能发生,两个线程同时添加数据导致 dataCache 大小大于允许的...

有没有一种无需添加复杂锁定机制即可完成此任务的优雅方法?

由于您在锁内更新 dataCacheSize,您可以在这里检查它是否保持正确:

if (dataCacheSize + retVal.Length <= maxDataCacheSize)
{
    lock (dataCacheSizeLockObj)
    {
        if (dataCacheSize + retVal.Length > maxDataCacheSize)
        {
            return retVal;
        }
        dataCacheSize += retVal.Length;
    }
    byte[] oldVal = dataCache.GetOrAdd(key, retVal);
    if (oldVal != retVal)
    {
        // retVal wasn't actually added
        lock (dataCacheSizeLockObj)
        {
            dataCacheSize -= retVal.Length;
        }
    }
}

与其尝试 "roll you own" 缓存,不如看看 System.Runtime.Caching.MemoryCache。见上面的评论。