Question

EDIT: From the answers I got already, I understand that the first solution that i presented, not really "Not blocking reads", since only one thread can enter upgradable lock and write lock can not be taken before read is released...

So my question, how to make in correct way the first solution to be "Non blocking read" with creation if not exists?


I'm trying to understand two solutions for non blocking multi threading reads. What is the difference between two solutions below (maybe I still not understand some things, but I'm trying):

/// <summary>
/// ReaderWriterLockSlim pattern
/// </summary>
public class ReadWriteLockCheck
{
    Dictionary<string, object> _dict = new Dictionary<string, object>();

    private ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);

    public void CreateByKey(string key)
    {
        _rwLock.EnterReadLock();
        try
        {
            if (!_dict.ContainsKey(key)) //Non blocking read - Check if exists
            {
                _rwLock.EnterWriteLock(); //Lock
                try
                {
                    _dict.Add(key, new object());
                }
                finally
                {
                    _rwLock.ExitWriteLock();
                }
            }
        }
        finally
        {
            _rwLock.ExitReadLock();
        }
    }

    public bool GetByKey(string key)
    {
        _rwLock.EnterWriteLock();
        try
        {
            if (_dict.ContainsKey(key)) //Non blocking read
            {
                return true;
            }

            return false;
        }
        finally
        {
            _rwLock.ExitReadLock();
        }
    }
}

/// <summary>
/// Double check lock pattern
/// </summary>
public class MonitorLock
{
    Dictionary<string, object> _dict = new Dictionary<string, object>();

    private object _syncObj = new Object();

    public void CreateByKey(string key)
    {
        if (!_dict.ContainsKey(key)) //Non blocking read - Check if exists
        {
            Monitor.Enter(_syncObj); //Lock
            try
            {
                if (!_dict.ContainsKey(key)) //Check if between first check and lock someone already added
                {
                    _dict.Add(key, new object());
                }
            }
            finally
            {
                Monitor.Exit(_syncObj);
            }
        }
    }

    public bool GetByKey(string key)
    {
        if (_dict.ContainsKey(key)) //Non blocking read
        {
            return true;
        }

        return false;
    }
}

As it looks for me, both of these solutions can make non blocking reads and only blocking when writing...if so, what is a benefit of ReaderWriterLockSlim? As I found in google, Monitor is much faster than ReaderWriterLockSlim. Of course I understand that possible I will get incorrect state of dictionary while reading, but it's OK for me.
Thanks

Was it helpful?

Solution

From MSDN:

Only one thread can enter upgradeable mode at any given time

basically, you haven't done much better than just using a full lock - except lock would actually have been faster.

Oddly enough, one good approach here is Hashtable; especially since the value is object, and the key is a reference-type (no extra boxing). Hashtable is unusual in that reads are fully thread-safe; you only need to guard against multiple writers.

For example:

readonly Hashtable lookup = new Hashtable();

...

object val = lookup[key]; // no need to synchronize when reading

...

lock(lookup)
{
    lookup[key] = newVal; // synchronize when writing
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top