HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Thread-safe object cache, for read, high performance in .NET

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
readhighcachethreadforsafeperformancenetobject

Problem

I want to realize an object cache which holds objects loaded from database, file or other slow access source, to make them available quickly with high performance. There will be a lot of accesses to get these objects from the dictionary!

It's not supposed to be Singleton/Static; multiple different object holders are possible, with multiple threads running on each.

Access is usually read-only, the objects themselves (in the dictionary) shall not be changed after being created.

For some special Admin functions, a Reload method exists, where thread safety can be ignored (not to be used by the main, multi-threaded program).

If I am right, the .NET Dictionary is thread-safe, as long as it is used only in read access, thus not requiring locks in reading methods. The dictionary will not be changed after being assigned to the _progObjects variable (do I need to make it volatile?). But time consuming volatile (alternatively Interlocked) access on the _areProgObjectsInitialized variable seems to be necessary, to make first accesses (with initialization) thread-safe.

Is the following a good and performant solution, can it still be improved, or are there safety risks?

```
///
/// Not Singleton, but each instance multi-threaded!
/// Holds objects loaded from an external source (database, file, whatever...)
///
public class ProgObjectsThreadSafeRead : IProgObjectsThreadSafeRead
{
private IDictionary _progObjects = null;
private object _progObjectsSyncObj = new object();
private volatile bool _areProgObjectsInitialized = false;

private void InitializeProgObjectsIfNeeded()
{
if (!_areProgObjectsInitialized)
{
lock(_progObjectsSyncObj)
{
if (!_areProgObjectsInitialized)
{
IDictionary localProgObj = LoadProgObjects();
// do something more
_progObjects = localProgObj;
_areProgObjectsInitialized = true;

Solution

the .NET Dictionary is thread-safe, as long as it is used only in read access, thus not requiring locks in reading methods.

You are playing with fire here. Your statement is only true iff no thread will modify the dictionary. If any of them do, which is about impossible to avoid in a cache, then a lock on both the reading and the writing code is required. A natural consequence of not being able to reliably read a data structure while it is in the process of being modified.

The ReaderWriterLock/Slim classes were designed to provide you this kind of locking. You must acquire a reader lock when you read, a writer lock when you write. Multiple threads can acquire a reader lock so you won't have any serious overhead as long as the cache is productive. The Slim version uses a low cost locking primitive which can reduce the lock cost somewhat. But it is no cure when there's a lot of write contention, no magic formula exists for that. If you do have a lot of write contention then double-check if the old ReaderWriterLock may give better throughput.

And be sure to checkout the .NET 4 MemoryCache class.

Context

StackExchange Code Review Q#11967, answer score: 7

Revisions (0)

No revisions yet.