3

I want to create a cache in my web application, which will allow the top layer (MVC) to persist some values, retrieved from underlying layers (services & DB), in order to avoid unnecessary requests to it. The data, that I want to store in cache, is needed on every page of the web site, so this cache is aimed to dramatically reduce the amount of requests. The idea is that a lot of threads will read the data from collection while one thread will clear it and retrieve new data after cache expiration. This means the collection will be used for frequent reading and rare writing operations.

The problem for me is to choose the appropriate class for these purposes. I have read about the set of concurrent classes from System.Collections.Concurrent, introduced in .NET 4. I definitely don't need to use ConcurrentQueue or ConcurrentStack, but I have to choose between BlockingCollection, ConcurrentBag and ConcurrentDictionary.

ConcurrentBag seems to be the best solution in this case. However, I read in this article that concurrent dictionary

... is entirely lock-free for read operations. In this way, it’s optimized for scenarios where reading from the dictionary is the most frequent operation.

So maybe the best solution is to use ConcurrentDictionary<int, MyTypeOfObj> instead? Or maybe I don't need concurrent type at all and simple List will do the job? Probably, it would do if I can somehow lock operations with it for the time of cache update. But using simple lock is undesirable.

Any advices and explanations are appreciated.

Update

The cache is used to store the map points of the outlets. The set of outlets is quite stable, but there should be a UI to add them, so insert operations are really rare. It might be even easier to retrieve whole collection from the underlying layer after timeout then perform insert operations. Search is not required as well, reading means simple enumeration.

4

1 回答 1

0

根据对问题的评论,我决定使用MemoryCache(事实上,我不太清楚)。由于我不需要单独的数据缓存实例,因此我使用保存在Default属性中的缓存:

除非需要,否则不要创建 MemoryCache 实例。(...) 如果您在应用程序中只创建一个缓存实例,请使用默认缓存并在需要访问缓存时从 Default 属性获取对它的引用。

链接 (MSDN)

我通过以下方式初始化缓存:

private static MemoryCache _instance = MemoryCache.Default;

public static IEnumerable<MyDto> GetDtos()
{
    var result = _instance.Get(SOME_NAME);
    if (result == null)
        result = InitializeCache();
    return result;
}

没有用于读取操作的锁。然后,一个锁定InitializeCache以防止多个请求:

private static IEnumerable<MyDto> InitializeCache()
{
    lock (_monitor) // here all threads but one will stop
    {
        var result = _instance.Get(SOME_NAME); // in case this thread is not the first who entered the lock
        if (result == null)
        {
            result = RetrieveSomeHowYourDataFromDbOrService();
            if (result == null)
                return null; // or exception
            _instance.Set(SOME_NAME, result, new CacheItemPolicy { AbsoluteExpiration = new DateTimeOffset(DateTime.Now.AddSeconds(TIMEOUT)), RemovedCallback = CacheClearedCallback}); 
        }
        return (IEnumerable<MyDto>)result;
    }
}

的使用CacheItemPolicy允许清除过期项目并从回调中再次检索项目:

private static void CacheClearedCallback(CacheEntryRemovedArguments arguments)
{
    InitializeCache();
}

在这种情况下,缓存将在没有消费者请求的情况下更新(而不是在应用程序的第一个请求中进行)。

可能,这不是有史以来最好的解决方案,但希望能帮助某人,也许是从开始。感谢您的提示和评论。

于 2014-06-08T17:48:12.770 回答