简体   繁体   中英

Concurrent collection to use for frequent read and rare write operations (.NET)

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.

Based on the comments to the question, I decided to use MemoryCache (whereof, in fact, I was not quite aware). Since I don't need separated cache instances for data, I use cache that is kept in Default property:

Do not create MemoryCache instances unless it is required. (...) If you create only a single cache instance in your application, use the default cache and get a reference to it from the Default property when you need to access the cache.

Link (MSDN)

Initializing of cache I made in the following way:

private static MemoryCache _instance = MemoryCache.Default;

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

No locks for reading operations. Then, one lock in InitializeCache to prevent multiple requests:

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

Use of CacheItemPolicy allows both clear out-of-date item and retrieve items again from the callback:

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

In this case cache will be updated without request from the consumer (not as it is made in the first request in application).

Probably, this is not the best solution ever, but hope will help somebody, maybe to start with. Thanks for tips and comments.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM